Write your first Go package for FPGAs
Reconfigure.io allows you to build and run your Go code on FPGAs in the cloud. In 15 minutes, you can have a reusable package written in Go, that you can deploy to FPGAs.
Things you’ll need
- A Github account
- A Travis CI account
- A local Go development environment
- The
reco
command line tool.
What we’ll do
- Create a local Go package
- Test it with our local Go development environment
- Create an example kernel to use on FPGAs
- Test it all with
reco check
- Create a command to test our kernel
- Setup Travis CI to test it on commit
Conventions
Whenever <your github username>
is used in a file, you'll need to replace that with your Github user name in your editor.
Create a package
To get started, in a shell, set the GH_USER
variable to your Github username.
$ export GH_USER=<your github user name>
Next, we’re going to create a folder for our package.
$ mkdir -p $GOPATH/src/github.com/$GH_USER/add
Create $GOPATH/src/github.com/$GH_USER/add/add.go
, with the following:
// Package add adds two numbers
package add// Add adds two numbers
func Add(a uint32, b uint32) uint32 {
return a + b
}
Now you can build your package with Go like so:
go build github.com/$GH_USER/add
Test your package
Create $GOPATH/src/github.com/$GH_USER/add/add_test.go
, with the following:
package addimport (
"testing"
)func TestAdd(t *testing.T){
result := Add(2, 3)
if result != 5 {
t.Errorf("Expected 5, got %d", result)
}
}
Now we can run those tests using go test
go test github.com/$GH_USER/add
Create an example kernel
$ mkdir -p $GOPATH/src/github.com/$GH_USER/add/kernels/add
Create $GOPATH/src/github.com/$GH_USER/add/kernels/add/main.go
with the following:
package mainimport (
// Import the entire framework (including bundled verilog)
_ "sdaccel" aximemory "axi/memory"
axiprotocol "axi/protocol" "github.com/<github user name>/add"
)func Top(
a uint32,
b uint32,
addr uintptr, // The second set of arguments will be the ports for interacting with memory
memReadAddr chan<- axiprotocol.Addr,
memReadData <-chan axiprotocol.ReadData, memWriteAddr chan<- axiprotocol.Addr,
memWriteData chan<- axiprotocol.WriteData,
memWriteResp <-chan axiprotocol.WriteResp) { // Since we're not reading anything from memory, disable those reads
go axiprotocol.ReadDisable(memReadAddr, memReadData) // Calculate the value
val := add.Add(a, b) // Write it back to the pointer the host requests
aximemory.WriteUInt32(
memWriteAddr, memWriteData, memWriteResp, false, addr, val)
}
This gives us a kernel that depends on our package. Next, we’ll need to vendor our package in order for our kernel to find it. Normally, we’d use other tools for this, but in this case, we can do it by hand.
$ mkdir -p $GOPATH/src/github.com/$GH_USER/add/kernels/add/vendor/github.com/$GH_USER/add
$ cp $GOPATH/src/github.com/$GH_USER/add/add.go $GOPATH/src/github.com/$GH_USER/add/kernels/add/vendor/github.com/$GH_USER/add/add.go
Now we can use reco
to check our code.
$ reco -s $GOPATH/src/github.com/$GH_USER/add/kernels/add check
Create a command to run our kernel
First, we’ll create the directory for a new command, called test-add.
$ mkdir -p $GOPATH/src/github.com/$GH_USER/add/kernels/add/cmd/test-add/
Create $GOPATH/src/github.com/$GH_USER/add/kernels/add/cmd/test-add/main.go
with the following:
package mainimport (
"encoding/binary"
"fmt"
"os"
"xcl"
)func main() {
// Allocate a world for interacting with kernels
world := xcl.NewWorld()
defer world.Release() // Import the kernel.
// Right now these two idenitifers are hard coded as an output from the build process
krnl := world.Import("kernel_test").GetKernel("reconfigure_io_sdaccel_builder_stub_0_1")
defer krnl.Release() // Allocate a buffer on the FPGA to store the return value of our computation
// The output is a uint32, so we need 4 bytes to store it
buff := world.Malloc(xcl.WriteOnly, 4)
defer buff.Free() // Pass the arguments to the kernel // Set the first operand to 1
krnl.SetArg(0, 1)
// Set the second operand to 2
krnl.SetArg(1, 2)
// Set the pointer to the output buffer
krnl.SetMemoryArg(2, buff) // Run the kernel with the supplied arguments
krnl.Run(1, 1, 1) // Decode that byte slice into the uint32 we're expecting
var ret uint32
err := binary.Read(buff.Reader(), binary.LittleEndian, &ret)
if err != nil {
fmt.Println("binary.Read failed:", err)
} // Print the value we got from the FPGA
fmt.Printf("%d\n", ret) // Exit with an error if the value is not correct
if ret != 3 {
os.Exit(1)
}
}
Now we can simulate this using reco
after we create a new project.
$ reco projects create add
$ cd $GOPATH/src/github.com/$GH_USER/add/kernels/add
$ reco projects set add
$ reco test run test-add
2017-10-05 14:37:43| preparing simulation .
2017-10-05 14:37:43| done
2017-10-05 14:37:43| archiving
2017-10-05 14:37:43| done
2017-10-05 14:37:43| uploading ..
2017-10-05 14:37:44| done
2017-10-05 14:37:44| running simulation
2017-10-05 14:37:44|
2017-10-05 14:37:44| you can run "reco simulation log 7767026d-9366-4a7e-b6c7-632c83844e5b" to manually stream logs
2017-10-05 14:37:44| getting simulation details
2017-10-05 14:37:44| status: QUEUED
2017-10-05 14:37:44| this may take several minutes
2017-10-05 14:37:44| waiting for simulation to start ...
$ cd -
Setup Travis CI
Create $GOPATH/src/github.com/$GH_USER/add/.travis.yml
with the following:
language: go
go_import_path: github.com/<github user name>/addgo:
- 1.9install:
- curl -LO https://s3.amazonaws.com/reconfigure.io/reco/releases/reco-v0.2.0-x86_64-linux.zip
- unzip reco-v0.2.0-x86_64-linux.zip
- sudo mv reco /usr/local/binscript:
- go test github.com/<github user name>/add
- mkdir -p kernels/add/vendor/github.com/<github user name>/add
- cp add.go kernels/add/vendor/github.com/<github user name>/add/add.go
- cd kernels/add && reco check
Now make a new repo on Github named “add”, initialized with a README and an open source license.
$ cd $GOPATH/src/github.com/$GH_USER/add
$ git init
$ git remote add origin git@github.com:$GH_USER/add.git
$ git fetch
$ git checkout -t origin/master
In your browser, visit Travis CI, click on your name, go to Accounts, and enable <github user name>/add
Now we’ll add everything and push
$ git add .travis.yml add.go add_test.go kernels/add/cmd/test-add/main.go kernels/add/main.go
$ git commit -m "Import example program"
$ git push
If you visit your Travis CI dashboard, you should see your project on the left hand side. It should start building shortly, if it hasn’t already. If you’ve done everything correctly, the build should be green, and you’ve published your first usable library for Reconfigure.io.