Skip to content

Commit

Permalink
Merge pull request #24 from ReconfigureIO/add/smi-histogram
Browse files Browse the repository at this point in the history
add version of histogram-array using SMI
  • Loading branch information
Rosie Yohannan authored Mar 8, 2018
2 parents 722a0b7 + e4a8171 commit ff160e1
Show file tree
Hide file tree
Showing 30 changed files with 6,559 additions and 1 deletion.
27 changes: 27 additions & 0 deletions histogram-array-SMI/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Array Histogram Example - using SMI protocol

In this example some random sample data is sent to the FPGA, it is then sorted into bins to create a histogram. The process of sorting the data has been parallelized to work efficiently on the FPGA – the sample data is held in an array, and while the FPGA reads the sample data in from the array it concurrently sorts the data and writes it back into a new channel.

## Structure

This directory contains code for an FPGA located at `main.go`. It also has a
command, `test-histogram` located at `cmd/test-histogram/main.go`

## Testing

To run this example in a simulator, execute the following:

```
reco test test-histogram
```

This will simulate the code running on an FPGA using a hardware simulator, and test it
using the `test-histogram` command.

## Building

```
reco build
```

This will build your commands and FPGA code for execution on hardware.
91 changes: 91 additions & 0 deletions histogram-array-SMI/cmd/test-histogram/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package main

import (
"encoding/binary"
"fmt"
"github.com/ReconfigureIO/sdaccel/xcl"
"log"
"math/rand"
"reflect"
)

// Define constants to be used in func main
const (
// The maximum bit width we allow values to have
MAX_BIT_WIDTH = 16
// The bit width we will compress to
HISTOGRAM_BIT_WIDTH = 9
// The resulting number of elements of the histogram
HISTOGRAM_WIDTH = 1 << 9
)

func main() {
// Allocate a 'world' for interacting with the FPGA
world := xcl.NewWorld()
defer world.Release()

// Import the compiled code that will be loaded onto the FPGA (referred to here as a kernel)
// Right now these two identifiers 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()

// Define a new array for the data we'll send to the FPGA for processing
input := make([]uint32, 20)

// Seed it with 20 random values, bound to 0 - 2**16
for i, _ := range input {
input[i] = uint32(uint16(rand.Uint32()))
}

// Allocate a space in the shared memory to store the data you're sending to the FPGA
buff := world.Malloc(xcl.ReadOnly, uint(binary.Size(input)))
defer buff.Free()

// Construct an array to hold the output data from the FPGA
var output [HISTOGRAM_WIDTH]uint32

// Allocate a space in the shared memory to store the output data from the FPGA
outputBuff := world.Malloc(xcl.ReadWrite, uint(binary.Size(output)))
defer outputBuff.Free()

// Write our input data to shared memory at the address we previously allocated
binary.Write(buff.Writer(), binary.LittleEndian, &input)

// Zero out the space in shared memory for the result from the FPGA
binary.Write(outputBuff.Writer(), binary.LittleEndian, &output)

// Pass the pointer to the input data in shared memory as the first argument
krnl.SetMemoryArg(0, buff)
// Pass the pointer to the memory location reserved for the result as the second argument
krnl.SetMemoryArg(1, outputBuff)
// Pass the total length of the input as the third argument
krnl.SetArg(2, uint32(len(input)))

// Run the FPGA with the supplied arguments. This is the same for all projects.
// The arguments ``(1, 1, 1)`` relate to x, y, z co-ordinates and correspond to our current
// underlying technology.
krnl.Run(1, 1, 1)

// Read the result from shared memory. If it is zero return an error
err := binary.Read(outputBuff.Reader(), binary.LittleEndian, &output)
if err != nil {
log.Fatal("binary.Read failed:", err)
}

// Calculate the same values locally to check the FPGA got it right
var expected [HISTOGRAM_WIDTH]uint32
for _, val := range input {
expected[val>>(MAX_BIT_WIDTH-HISTOGRAM_BIT_WIDTH)] += 1
}

// Return an error if the local and FPGA calculations do not give the same result
if !reflect.DeepEqual(expected, output) {
log.Fatalf("%v != %v\n", output, expected)
}

// Print out each bin and coresponding value
for i, val := range output {
fmt.Printf("%d: %d\n", i<<(MAX_BIT_WIDTH-HISTOGRAM_BIT_WIDTH), val)
}

}
9 changes: 9 additions & 0 deletions histogram-array-SMI/glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions histogram-array-SMI/glide.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package: .
import:
- package: github.com/ReconfigureIO/sdaccel
subpakages:
- smi
- xcl
58 changes: 58 additions & 0 deletions histogram-array-SMI/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
// import the entire framework (including bundled verilog)
_ "github.com/ReconfigureIO/sdaccel"

// Use the new SMI protocol package
"github.com/ReconfigureIO/sdaccel/smi"
)

// function to calculate the bin for each sample
func CalculateIndex(sample uint32) uint16 {
return uint16(sample) >> (16 - 9)
}

// magic identifier for exporting
func Top(
// Three operands from the host. Pointers to the input data and the space for the result in shared
// memory and the length of the input data so the FPGA knows what to expect.
inputData uintptr,
outputData uintptr,
length uint32,

// Set up channels for interacting with the shared memory
readReq chan<- smi.Flit64,
readResp <-chan smi.Flit64,

writeReq chan<- smi.Flit64,
writeResp <-chan smi.Flit64) {

// Create an array to hold the histogram data as it is sorted
histogram := [512]uint32{}

// Read all of the input data into a channel
inputChan := make(chan uint32)
go smi.ReadBurstUInt32(readReq, readResp, inputData, smi.DefaultOptions, length, inputChan)

// The host needs to provide the length we should read
for ; length > 0; length-- {
// First we'll pull of each sample from the channel
sample := <-inputChan

// And increment the value in the correct bin using the calculation function
histogram[CalculateIndex(sample)] += 1
}

// Write the results to a new channel
data := make(chan uint32)
go func() {
for i := 0; i < 512; i++ {
data <- histogram[i]
}
}()

// Write the results to shared memory
smi.WriteBurstUInt32(
writeReq, writeResp, outputData, smi.DefaultOptions, 512, data)
}
17 changes: 17 additions & 0 deletions histogram-array-SMI/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"testing"
"testing/quick"
)

func TestCalculateIndexDoesNotOutOfBounds(t *testing.T) {
// Check that we never generate an index out of bounds
f := func(x uint32) bool {
index := CalculateIndex(x)
return index < 512
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
2 changes: 2 additions & 0 deletions histogram-array-SMI/reco.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
memory_interface: smi
ports: 2

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit ff160e1

Please sign in to comment.