Introduction
This example will show a simple implementation (without optimization) of a Mandelbrot fractal calculator by using data streams and fixed point calculations.
Specification
The component will receive one Stream
of pixel tasks (which contain the XY coordinates in the Mandelbrot space) and will produce one Stream
of pixel results (which contain the number of iterations done for the corresponding task).
Let’s specify the IO of our component:
IO Name | Direction | Type | Description |
---|---|---|---|
cmd | slave | Stream[PixelTask] | Provide XY coordinates to process |
rsp | master | Stream[PixelResult] | Return iteration count needed for the corresponding cmd transaction |
Let’s specify the PixelTask Bundle
:
Element Name | Type | Description |
---|---|---|
x | SFix | Coordinate in the Mandelbrot space |
y | SFix | Coordinate in the Mandelbrot space |
Let’s specify the PixelResult Bundle
:
Element Name | Type | Description |
---|---|---|
iteration | UInt | Number of iterations required to solve the Mandelbrot coordinates |
Elaboration parameters (Generics)
Let’s define the class that will provide construction parameters of our system:
case class PixelSolverGenerics(fixAmplitude : Int,
fixResolution : Int,
iterationLimit : Int){
val iterationWidth = log2Up(iterationLimit+1)
def iterationType = UInt(iterationWidth bits)
def fixType = SFix(
peak = fixAmplitude exp,
resolution = fixResolution exp
)
}
Note: iterationType and fixType are functions that you can call to instantiate new signals. It’s like a typedef in C.
Bundle definition
case class PixelTask(g : PixelSolverGenerics) extends Bundle{
val x,y = g.fixType
}
case class PixelResult(g : PixelSolverGenerics) extends Bundle{
val iteration = g.iterationType
}
Component implementation
And now the implementation. The one below is a very simple one without pipelining / multi-threading.
case class PixelSolver(g : PixelSolverGenerics) extends Component{
val io = new Bundle{
val cmd = slave Stream(PixelTask(g))
val rsp = master Stream(PixelResult(g))
}
import g._
//Define states
val x,y = Reg(fixType) init(0)
val iteration = Reg(iterationType) init(0)
//Do some shared calculation
val xx = x*x
val yy = y*y
val xy = x*y
//Apply default assignement
io.cmd.ready := False
io.rsp.valid := False
io.rsp.iteration := iteration
when(io.cmd.valid) {
//Is the mandelbrot iteration done ?
when(xx + yy >= 4.0 || iteration === iterationLimit) {
io.rsp.valid := True
when(io.rsp.ready){
io.cmd.ready := True
x := 0
y := 0
iteration := 0
}
} otherwise {
x := (xx - yy + io.cmd.x).truncated
y := (((xy) << 1) + io.cmd.y).truncated
iteration := iteration + 1
}
}
}