Browse Source

oooo

master
peteraa 7 years ago
parent
commit
e52f50904c
11 changed files with 900 additions and 62 deletions
  1. +1
    -1
      TODOs.org
  2. +24
    -6
      ov0/src/main/scala/Tile.scala
  3. +157
    -0
      ov0/src/main/scala/basics.scala
  4. +40
    -0
      ov0/src/main/scala/daisyDot.scala
  5. +9
    -20
      ov0/src/main/scala/daisyGrid.scala
  6. +3
    -4
      ov0/src/main/scala/daisyMatMul.scala
  7. +1
    -19
      ov0/src/main/scala/daisyVec.scala
  8. +117
    -0
      ov0/src/main/scala/daisyVecMat.scala
  9. +40
    -0
      ov0/src/main/scala/daisyVecVec.scala
  10. +191
    -12
      ov0/src/main/scala/oppgavetekst.org
  11. +317
    -0
      ov0/src/test/scala/tests.scala

+ 1
- 1
TODOs.org View File

@@ -5,7 +5,7 @@
Finn ut hvordan bundles, defs etc burde fungere.
* Now
** TODO Port Babby to chisel3
** DONE Port Babby to chisel3
*** DONE compile and run
*** DONE fix deprecation


+ 24
- 6
ov0/src/main/scala/Tile.scala View File

@@ -6,12 +6,9 @@ import chisel3.iotesters.PeekPokeTester

object CoreMain {
def main(args: Array[String]): Unit = {
// chiselMainTest(args, () => Module(new daisyVector(4, 32))) { c => new daisyVectorTest(c) }
// chiselMainTest(args, () => Module(new daisyGrid(4, 3, 32))) { c => new daisyGridTest(c) }
// chiselMainTest(args, () => Module(new daisyMultiplier(3, 2, 2, 3, 32))) { c => new daisyMultiplierTest(c) }

iotesters.Driver.execute(args, () => new daisyMultiplier(3, 2, 2, 3, 32)) {
c => new daisyMultiplierTest(c)
iotesters.Driver.execute(args, () => new mySelector(10)){
c => new mySelectorTest(c)
}
}
}
@@ -59,7 +56,7 @@ object Extras {
val vecA = List(1, 2, 4)
val vecB = List(2, -3, 1)

val dotProductForLoop = {
def dotProductForLoop(vecA: List[Int], vecB: List[Int]) = {
var dotProduct = 0
for(i <- 0 until vecA.length){
dotProduct = dotProduct + (vecA(i) * vecB(i))
@@ -81,4 +78,25 @@ object Extras {
// This is not good code!!!
val tooFancyDotProduct =
(0 /: (vecA zip vecB)){ case(acc, ab) => acc + (ab._1 * ab._2) }


type Matrix[A] = List[List[A]]
def vectorMatrixMultiply(vec: List[Int], matrix: Matrix[Int]): List[Int] = {
val transposed = matrix.transpose

val outputVector = Array.ofDim[Int](vec.length)
for(ii <- 0 until matrix.length){
outputVector(ii) = dotProductForLoop(vec, transposed(ii))
}
outputVector.toList
}


val vec = List(1, 0, 1)
val matrix = List(
List(2, 1, 2),
List(3, 2, 3),
List(4, 1, 1)
)
println(vectorMatrixMultiply(vec, matrix))
}

+ 157
- 0
ov0/src/main/scala/basics.scala View File

@@ -0,0 +1,157 @@

package Core
import chisel3._
import chisel3.core.Input
import chisel3.iotesters.PeekPokeTester


class myIncrement(incrementBy: Int) extends Module {
val io = IO(
new Bundle {
val dataIn = Input(UInt(32.W))
val dataOut = Output(UInt(32.W))
}
)

io.dataOut := io.dataIn + incrementBy.U
}


class myIncrementTwice(incrementBy: Int) extends Module {
val io = IO(
new Bundle {
val dataIn = Input(UInt(32.W))
val dataOut = Output(UInt(32.W))
}
)

val first = Module(new myIncrement(incrementBy))
val second = Module(new myIncrement(incrementBy))

first.io.dataIn := io.dataIn
second.io.dataIn := first.io.dataOut

io.dataOut := second.io.dataOut
}


class myIncrementN(incrementBy: Int, numIncrementors: Int) extends Module {
val io = IO(
new Bundle {
val dataIn = Input(UInt(32.W))
val dataOut = Output(UInt(32.W))
}
)

val incrementors = Array.fill(numIncrementors){ Module(new myIncrement(incrementBy)) }

for(ii <- 1 until numIncrementors){
incrementors(ii).io.dataIn := incrementors(ii - 1).io.dataOut
}

incrementors(0).io.dataIn := io.dataIn
io.dataOut := incrementors(numIncrementors).io.dataOut
}




class myDelay() extends Module {
val io = IO(
new Bundle {
val dataIn = Input(UInt(32.W))
val dataOut = Output(UInt(32.W))
}
)

val reg = RegInit(UInt(32.W), 0.U)
reg := io.dataIn
io.dataOut := reg
}


class myDelayN(steps: Int) extends Module {
val io = IO(
new Bundle {
val dataIn = Input(UInt(32.W))
val dataOut = Output(UInt(32.W))
}
)

val delayers = Array.fill(steps){ Module(new myDelay()) }

for(ii <- 1 until steps){
delayers(ii).io.dataIn := delayers(ii - 1).io.dataOut
}

delayers(0).io.dataIn := io.dataIn
io.dataOut := delayers(steps).io.dataOut
}


class mySelector(numValues: Int) extends Module {
val io = IO(
new Bundle {
val next = Input(Bool())
val dataOut = Output(UInt(32.W))
val newOutput = Output(Bool())
}
)

val counter = RegInit(UInt(Chisel.log2Up(numValues).W), 0.U)
val nextOutputIsFresh = RegInit(Bool(), true.B)

// Generate random values. Using the when keyword we choose which random
// value should drive the dataOut signal
io.dataOut := 0.U
List.fill(numValues)(scala.util.Random.nextInt(100)).zipWithIndex.foreach {
case(rand, idx) =>
when(counter === idx.U){
if(rand < 50)
io.dataOut := rand.U
else
io.dataOut := (rand + 100).U
}
}

// While chisel comes with an inbuilt Counter, we implement ours the old fashion way
// There are far more elegant ways of implementing this, read the chisel docs, discuss
// best practice among yourselves and experiment!
nextOutputIsFresh := true.B
when(io.next === true.B){
when(counter < (numValues - 1).U){
counter := counter + 1.U
}.otherwise {
counter := 0.U
}
}.otherwise {
nextOutputIsFresh := false.B
}
io.newOutput := nextOutputIsFresh
}


class mySelectorTest(c: mySelector) extends PeekPokeTester(c) {
poke(c.io.next, true.B)
for(ii <- 0 until 10){
val wasStale = peek(c.io.newOutput) == 0
val output = peek(c.io.dataOut).toString()
println(s"at step $ii:")
println(s"data out is $output")
println(s"was the output fresh? ${!wasStale}")
println()
step(1)
}

poke(c.io.next, false.B)

for(ii <- 0 until 3){
val wasStale = peek(c.io.newOutput) == 0
val output = peek(c.io.dataOut).toString()
println(s"at step $ii:")
println(s"data out is $output")
println(s"was the output fresh? ${!wasStale}")
println()
step(1)
}
}

+ 40
- 0
ov0/src/main/scala/daisyDot.scala View File

@@ -0,0 +1,40 @@
package Core
import chisel3._
import chisel3.core.Input
import chisel3.iotesters.PeekPokeTester
import chisel3.util.Counter

/**
DaisyVectors are not indexed. They have no control inputs or outputs, only data.
*/
class daisyDot(elements: Int, dataWidth: Int) extends Module{

val io = IO(new Bundle {
val dataInA = Input(UInt(dataWidth.W))
val dataInB = Input(UInt(dataWidth.W))

val dataOut = Output(UInt(dataWidth.W))
val outputValid = Output(Bool())
})

val counter = Counter(elements)
val accumulator = RegInit(UInt(dataWidth.W), 0.U)

/**
Your implementation here
*/

/**
LF
*/
val product = io.dataInA * io.dataInB
when(counter.inc()){
io.outputValid := true.B
accumulator := 0.U
}.otherwise{
io.outputValid := false.B
accumulator := accumulator + product
}

io.dataOut := accumulator + product
}

+ 9
- 20
ov0/src/main/scala/daisyGrid.scala View File

@@ -13,7 +13,7 @@ class daisyGrid(rows: Int, cols: Int, dataWidth: Int) extends Module{

val readEnable = Input(Bool())
val dataIn = Input(UInt(dataWidth.W))
val readRow = Input(UInt(8.W))
val rowSelect = Input(UInt(8.W))

val dataOut = Output(UInt(dataWidth.W))
})
@@ -25,6 +25,13 @@ class daisyGrid(rows: Int, cols: Int, dataWidth: Int) extends Module{
val elements = rows*cols


/**
Your implementation here
*/

/**
LF
*/
io.dataOut := 0.U

for(ii <- 0 until rows){
@@ -32,27 +39,9 @@ class daisyGrid(rows: Int, cols: Int, dataWidth: Int) extends Module{
memRows(ii).readEnable := 0.U
memRows(ii).dataIn := io.dataIn

when(io.readRow === ii.U ){
when(io.rowSelect === ii.U ){
memRows(ii).readEnable := io.readEnable
io.dataOut := memRows(ii).dataOut
}
}
}

class daisyGridTest(c: daisyGrid) extends PeekPokeTester(c) {

poke(c.io.readEnable, 1)
for(ii <- 0 until 12){
poke(c.io.dataIn, ii)
poke(c.io.readRow, ii/3)
step(1)
println("////////////////////")
}
poke(c.io.readEnable, 0)
for(ii <- 0 until 12){
peek(c.io.dataOut)
poke(c.io.readRow, ii/3)
step(1)
println("////////////////////")
}
}

+ 3
- 4
ov0/src/main/scala/daisyMatMul.scala View File

@@ -32,7 +32,6 @@ class daisyMultiplier(val rowsA: Int, val colsA: Int, val rowsB: Int, val colsB:

val resultReady = RegInit(Bool(), false.B)

println(s"rowsA: $rowsA, colsA: $colsA, rowsB: $rowsB, colsB: $colsB")

////////////////////////////////////////
////////////////////////////////////////
@@ -85,12 +84,12 @@ class daisyMultiplier(val rowsA: Int, val colsA: Int, val rowsB: Int, val colsB:
////////////////////////////////////////
/// set up reading patterns depending on if we are in calculating state or not
when(calculating === true.B){
matrixA.readRow := rowOutputCounter
matrixA.rowSelect := rowOutputCounter
}.otherwise{
matrixA.readRow := rowCounter
matrixA.rowSelect := rowCounter
}

matrixB.readRow := rowCounter
matrixB.rowSelect := rowCounter





+ 1
- 19
ov0/src/main/scala/daisyVec.scala View File

@@ -17,7 +17,7 @@ class daisyVector(elements: Int, dataWidth: Int) extends Module{

val currentIndex = RegInit(UInt(8.W), 0.U)

val memory = Array.fill(elements)(Reg(UInt(dataWidth.W)))
val memory = Array.fill(elements)(RegInit(UInt(dataWidth.W), 0.U))

when(currentIndex === (elements - 1).U ){
currentIndex := 0.U
@@ -37,21 +37,3 @@ class daisyVector(elements: Int, dataWidth: Int) extends Module{
}
}
}

class daisyVectorTest(c: daisyVector) extends PeekPokeTester(c) {

poke(c.io.readEnable, 1)
step(1)

for(ii <- 0 until 4){
poke(c.io.dataIn, ii)
println("////////////////////")
step(1)
}

poke(c.io.readEnable, 0)
for(ii <- 0 until 4){
peek(c.io.dataOut)
step(1)
}
}

+ 117
- 0
ov0/src/main/scala/daisyVecMat.scala View File

@@ -0,0 +1,117 @@

package Core
import Core.daisyVector
import chisel3._
import chisel3.core.Input
import chisel3.iotesters.PeekPokeTester
import chisel3.util.Counter

/**
The daisy multiplier creates two daisy grids, one transposed, and multiplies them.
*/
class daisyVecMat(val lengthA: Int, val rowsB: Int, val colsB: Int, val dataWidth: Int) extends Module {

val io = IO(new Bundle {

val dataInA = Input(UInt(dataWidth.W))
val readEnableA = Input(Bool())

val dataInB = Input(UInt(dataWidth.W))
val readEnableB = Input(Bool())

val dataOut = Output(UInt(dataWidth.W))
val dataValid = Output(Bool())
val done = Output(Bool())
})

// How many cycles does it take to fill the matrices with data?


////////////////////////////////////////
////////////////////////////////////////
/// We transpose matrix B.
val vecA = Module(new daisyVector(lengthA, dataWidth)).io
val matrixB = Module(new daisyGrid(colsB, rowsB, dataWidth)).io
val dotProductCalculator = Module(new daisyDot(lengthA, dataWidth)).io
val dataIsLoaded = RegInit(Bool(), false.B)

/**
Your implementation here
*/

/**
LF
*/
val dataValid = Wire(Bool())


////////////////////////////////////////
////////////////////////////////////////
/// Wire components
vecA.dataIn := io.dataInA
vecA.readEnable := io.readEnableA

matrixB.dataIn := io.dataInB
matrixB.readEnable := io.readEnableB

io.dataOut := dotProductCalculator.dataOut

// allows us to use dataValid internally
io.dataValid := dataValid

dotProductCalculator.dataInA := vecA.dataOut
dotProductCalculator.dataInB := matrixB.dataOut
dataValid := dotProductCalculator.outputValid & dataIsLoaded

////////////////////////////////////////
////////////////////////////////////////
/// Select the correct row
val (currentCol, colDone) = Counter(true.B, colsB)
val (rowSel, _) = Counter(colDone, rowsB)
matrixB.rowSelect := rowSel


////////////////////////////////////////
////////////////////////////////////////
/// Check if data is loaded
val aReady = RegInit(Bool(), false.B)
val bReady = RegInit(Bool(), false.B)

val (inputCounterA, counterAWrapped) = Counter(io.readEnableA, lengthA - 1)
when(counterAWrapped){ aReady := true.B }

val (inputCounterB, counterBWrapped) = Counter(io.readEnableB, colsB*rowsB)
when(counterBWrapped){ bReady := true.B }

dataIsLoaded := aReady & bReady


////////////////////////////////////////
////////////////////////////////////////
/// Check if we're done
val isDone = RegInit(Bool(), false.B)
val (numOutputted, numOutputtedWrapped) = Counter(dataValid, lengthA)

when(numOutputtedWrapped){ isDone := true.B }


// printf(p"dataInA = ${io.dataInA}\n")
// printf(p"validA = ${io.readEnableA}\n")
// printf(p"dataInB = ${io.dataInB}\n")
// printf(p"validB = ${io.readEnableB}\n")
// printf(p"validOut = ${io.dataValid}\n")
// printf(p"data loaded = ${dataIsLoaded}\n")
// printf(p"aReady = ${aReady}\n")
// printf(p"bReady = ${bReady}\n")

// printf(p"counter A = ${inputCounterA}\n")
// printf(p"counter B = ${inputCounterB}\n")

// printf(p"out = ${dotProductCalculator.dataOut}\n\n")




io.done := isDone

}

+ 40
- 0
ov0/src/main/scala/daisyVecVec.scala View File

@@ -0,0 +1,40 @@
package Core
import chisel3._
import chisel3.core.Input
import chisel3.iotesters.PeekPokeTester
import chisel3.util.Counter

/**
DaisyVectors are not indexed. They have no control inputs or outputs, only data.
*/
class daisyVecVec(elements: Int, dataWidth: Int) extends Module{

val io = IO(new Bundle {
val dataInA = Input(UInt(dataWidth.W))
val dataInB = Input(UInt(dataWidth.W))

val dataOut = Output(UInt(dataWidth.W))
val outputValid = Output(Bool())
})

val counter = Counter(elements)
val accumulator = RegInit(UInt(dataWidth.W), 0.U)

/**
Your implementation here
*/

/**
LF
*/
val product = io.dataInA * io.dataInB
when(counter.inc()){
io.outputValid := true.B
accumulator := 0.U
}.otherwise{
io.outputValid := false.B
accumulator := accumulator + product
}

io.dataOut := accumulator + product
}

+ 191
- 12
ov0/src/main/scala/oppgavetekst.org View File

@@ -4,12 +4,142 @@
In this exercise you will implement a circuit capable of performing matrix
matrix multiplication in the chisel hardware description language.

* Your first component
There are two types of digital components: Combinatorial and stateful.
The first component we will consider is a simple combinatorial incrementor:
#+begin_src scala
class myIncrement(incrementBy: Int) extends Module {
val io = IO(
new Bundle {
val dataIn = Input(UInt(32.W))
val dataOut = Output(UInt(32.W))
}
)

io.dataOut := io.dataIn + incrementBy.U
#+end_src
Let's break the code down down. First, myIncrement is a Module, meaning that
this class can be instantiated as a hardware circuit.
Figure [rm3] shows the model that you have just declared.
A 32 bit signal, data_in goes in, and another 32 bit signal goes out.
Apart from the IO, there is only one statement, assigning dataOut to dataIn +
incrementBy.
In RTL the component looks like fig [rm4]
Let's see how we can use our module:
#+begin_src scala
class myIncrementTwice(incrementBy: Int) extends Module {
val io = IO(
new Bundle {
val dataIn = Input(UInt(32.W))
val dataOut = Output(UInt(32.W))
}
)

val first = Module(new myIncrement(incrementBy))
val second = Module(new myIncrement(incrementBy))

first.io.dataIn := io.dataIn
second.io.dataIn := first.io.dataOut

io.dataOut := second.io.dataOut
}
#+end_src
Fig [rm5] shows the RTL design, as expected it's just two incrementors
chained.
The following code shows off how you can use for loops to instantiate an
arbitrary amount of modules.
#+begin_src scala
class myIncrementN(incrementBy: Int, numIncrementors: Int) extends Module {
val io = IO(
new Bundle {
val dataIn = Input(UInt(32.W))
val dataOut = Output(UInt(32.W))
}
)

val incrementors = Array.fill(numIncrementors){ Module(new myIncrement(incrementBy)) }

for(ii <- 1 until numIncrementors){
incrementors(ii).io.dataIn := incrementors(ii - 1).io.dataOut
}

incrementors(0).io.dataIn := io.dataIn
io.dataOut := incrementors(numIncrementors).io.dataOut
}
#+end_src
Keep in mind that the for-loop only exists at design time, just like a for loop
generating a table in HTML will not be part of the finished HTML.
So, what does combinatorial mean?
To answer that, let's create a stateful circuit first.

#+begin_src scala
class myDelay() extends Module {
val io = IO(
new Bundle {
val dataIn = Input(UInt(32.W))
val dataOut = Output(UInt(32.W))
}
)
val delayReg = RegInit(UInt(32.W), 0.U)

delayReg := io.dataIn
io.dataOut := delayReg
}
#+end_src
This circuit seems rather pointless, it simply assigns the input to the output.
However, the register has another input, as seen in the RTL: fig [rm6].
The register can only change value during rising edges on the clock!
To examplify, assume at step 0 data in is 0x45.
delayReg will now have 0x45 as its data in, but data out will still be 0.
Only when the clock ticks will delayReg.dataOut take on the value 0x45.
You should now be able to implement myDelayN following the same principles as
myIncrementN
#+begin_src scala
class myDelayN(delay: Int) extends Module {
val io = IO(
new Bundle {
val dataIn = Input(UInt(32.W))
val dataOut = Output(UInt(32.W))
}
)
???
}
#+end_src

This should answer the initial question of combinatorial vs stateful:
The output of a combinatorial circuit will be available instantly, while
a stateful circuit will only update its output during rising edges on the
clock.
Before you continue it is recommended that you check out the chisel3
tutorials.
In the basics.scala there is one more module, a basic selector.
At compile time this component builds n random numbers, to see which we can
cycle through them.
The component comes with a test, this test will be run when you do sbt.run
You should study this component. What is the difference between if/else and
when/otherwise?
* Matrix matrix multiplication
When designing digital logic you should always start with decomposition.
Your first task is therefore to implement a dot product calculator, since
a matrix matrix multiplication is essentially a series of these.
* Dot Product calculator
* Dot Prod
First, let's consider how a dot product calculator would look like in regular
scala:
@@ -25,20 +155,69 @@
}
dotProduct
}

// Scala has rich support for functional programming
val dotProductFP = (vecA zip vecB)
.map{ case(a, b) => a*b }
.sum
#+end_src

In the for loop version you can see how the dot product is sequentially
In the for loop you can see how the dot product is sequentially
calculated by multiplying vector values of the same indice and summing the
result.
The dot product for loop works in a similar fashion to your first design, but
before you get there some basics are in order.
* Your first component
There are two types of digital components: Combinatorial and stateful.
To implement this logic in hardware the first thing you need is some way to
represent a vector which is your first task.
** Task 1 - Vector
The first component you should implement is a register bank for storing a vector.
This module works as follows:
let dataOut(T) = if (T - vectorLength) < 0 then 0 else
if enableIn(T - vectorLength)
then dataIn(T - vectorLength)
else
dataOut(T - vectorLength)
From the figure the principle of operation becomes clearer [inkskape drawing, rm sketch]
To test your implementation you can run testOnly Core.daisyVecSpec in sbt
** Task 2 - Dot Product
Your next task is to implement daisyDot.
daisyDot should calculate the dot product of two vectors, inA and inB. Ensure that validOut
is only asserted when you have a result. Ensure that your accumulator gets flushed after
calculating your dot product.

** Task 3 - Vector Matrix multiplication
Having implemented a dot product calculator, a vector matrix multiplier is not that different.
In imperative code we get something like this:
#+begin_src scala
type Matrix[A] = List[List[A]]
def vectorMatrixMultiply(vec: List[Int], matrix: Matrix[Int]): List[Int] = {
val transposed = matrix.transpose
val outputVector = Array.ofDim[Int](vec.length)
for(ii <- 0 until matrix.length){
outputVector(ii) = dotProductForLoop(vec, transposed(ii))
}
outputVector.toList
}
#+end_src scala
This is just repeated application of dotProduct.
Since vector matrix multiplication is the dotproduct of the vector and the rows of the matrix,
the matrix must be transposed.
*** Subtask 1 - representing a matrix
Like the dot product calculator, the first step is to implement a register bank for storing a matrix.
This can be done by creating n vectors from Task 1 and then select which row is the 'current' row.
The matrix representation you have created in this task allows you to select which row to read, but
not which column. This isn't very efficient when you want to read an entire column since you would have
to wait a full cycle for each row.
The way we deal with this is noticing that when multiplying two matrices we work on a row basis in
matrix A, and column basis on matrix B. If we simply transpose matrix B, then accessing its rows is
the same as accessing the columns of matrix B.
A consequence of this is that the API exposed by your matrix multiplier requires matrix B to be transposed.

*** Subtask 2 - vector matrix multiplication

+ 317
- 0
ov0/src/test/scala/tests.scala View File

@@ -0,0 +1,317 @@
package Core
import chisel3._
import chisel3.util._
import chisel3.core.Input
import chisel3.iotesters._
import org.scalatest.{Matchers, FlatSpec}


class daisyVectorTest(c: daisyVector, inputs: List[(Int, Int, Int)]) extends PeekPokeTester(c) {

(inputs).foreach {
case(enIn, dataIn, dataOut) => {
poke(c.io.readEnable, enIn)
poke(c.io.dataIn, dataIn)
expect(c.io.dataOut, dataOut)
step(1)
}
}

}

class daisyDotTest(c: daisyDot, inputs: List[(Int, Int, Option[Int], Int)]) extends PeekPokeTester(c) {

(inputs).foreach {
case(inA, inB, dataOut, dataValid) => {
poke(c.io.dataInA, inA)
poke(c.io.dataInB, inB)
dataOut.foreach { expect(c.io.dataOut, _) }
expect(c.io.outputValid, dataValid)
step(1)
}
}

}

class daisyGridTest(c: daisyGrid, inputs: List[(Int, Int, Int, Int)]) extends PeekPokeTester(c) {

(inputs).foreach {
case(readEnable, dataIn, readRow, dataOut) => {
poke(c.io.readEnable, readEnable)
poke(c.io.dataIn, dataIn)
poke(c.io.rowSelect, readRow)
expect(c.io.dataOut, dataOut)
step(1)
}
}

}


class daisyVecMatTest(c: daisyVecMat, inputs: List[(Int,Int,Int,Int,Option[Int],Int,Int)]) extends PeekPokeTester(c) {

(inputs).foreach {
case(dataInA, readEnableA, dataInB, readEnableB, dataOutExpect, dataValidExpect, doneExpect) => {
poke(c.io.dataInA, dataInA)
poke(c.io.dataInB, dataInB)
poke(c.io.readEnableA, readEnableA)
poke(c.io.readEnableB, readEnableB)
expect(c.io.dataValid, dataValidExpect)
expect(c.io.done, doneExpect)
dataOutExpect.foreach { expect(c.io.dataOut, _) }
step(1)
}
}

}


class daisyVecSpec extends FlatSpec with Matchers {

val input1 = List.fill(10)((0, 0x45, 0))

val input2 = input1 ++ List(
// enableIn, dataIn, expected
(1, 2, 0),
(1, 2, 0),
(1, 2, 0),
(1, 2, 0),
(0, 0, 2),
(0, 0, 2),
(0, 0, 2),
(0, 0, 2),
(0, 0, 2),
(0, 0, 2),
(0, 0, 2),
(0, 0, 2))


val input3 = {
val inputs = List.fill(100)(scala.util.Random.nextInt(10000))
val withExpected = (List.fill(4)(0) ++ inputs) zip inputs
val withEnabled = withExpected.map{ case(expected, in) => (1, in, expected) }

withEnabled
}

behavior of "daisy vector"

it should "not read when read enable is low" in {
iotesters.Driver.execute(() => new daisyVector(4, 32), new TesterOptionsManager) { c =>
new daisyVectorTest(c, input1)
} should be(true)
}


it should "read only when read enable is asserted" in {
iotesters.Driver.execute(() => new daisyVector(4, 32), new TesterOptionsManager) { c =>
new daisyVectorTest(c, input2)
} should be(true)
}


it should "Work in general" in {
iotesters.Driver.execute(() => new daisyVector(4, 32), new TesterOptionsManager) { c =>
new daisyVectorTest(c, input3)
} should be(true)
}
}


class daisyDotSpec extends FlatSpec with Matchers {
behavior of "daisy vector"

val input1 = List(
(0, 0, None, 0),
(0, 0, None, 0),
(0, 0, None, 1),
(0, 0, None, 0),
(0, 0, None, 0),
(0, 0, None, 1),
(0, 0, None, 0),
(0, 0, None, 0),
(0, 0, None, 1))

it should "Only signal valid output at end of calculation" in {
iotesters.Driver.execute(() => new daisyDot(3, 32), new TesterOptionsManager) { c =>
new daisyDotTest(c, input1)
} should be(true)
}


val input2 = List(
(1, 0, None, 0),
(1, 0, None, 0),
(1, 0, Some(3), 1),
(1, 0, None, 0),
(1, 0, None, 0),
(1, 0, Some(3), 1),
(1, 0, None, 0),
(1, 0, None, 0),
(1, 0, Some(3), 1))

it should "Be able to count to 3" in {
iotesters.Driver.execute(() => new daisyDot(3, 32), new TesterOptionsManager) { c =>
new daisyDotTest(c, input1)
} should be(true)
}

def createProblem(vecLen: Int): List[(Int, Int, Option[Int], Int)] = {
val in1 = List.fill(vecLen)(scala.util.Random.nextInt(10))
val in2 = List.fill(vecLen)(scala.util.Random.nextInt(10))

val dotProduct = (in1, in2).zipped.map(_*_).sum

(in1, in2, (0 to vecLen)).zipped.map{
case(a, b, idx) =>
val dpExpect = if(idx == (vecLen - 1)) Some(dotProduct) else None
val outExpect = if(idx == (vecLen - 1)) 1 else 0

(a, b, dpExpect, outExpect)
}
}


def createProblems(vecLen: Int): List[(Int, Int, Option[Int], Int)] =
List.fill(10)(createProblem(vecLen)).flatten


it should "Be able to calculate dot products" in {
iotesters.Driver.execute(() => new daisyDot(10, 32), new TesterOptionsManager) { c =>
new daisyDotTest(c, createProblems(10))
} should be(true)
}
}


class daisyGridSpec extends FlatSpec with Matchers {
type Matrix[A] = List[List[A]]

behavior of "daisy grid"

def genMatrix(dims: (Int,Int)): Matrix[Int] =
List.fill(dims._1)(
List.fill(dims._2)(scala.util.Random.nextInt(100))
)

def readRowCheck(dims: (Int,Int)): List[(Int,Int,Int,Int)] = {
// readEn, dataIn, readRow, expected dataOut
List.fill(dims._1 - 1)(( 1, 1, 0, 0)) ++
List.fill(dims._1 - 1)((0, 0, 0, 1)) ++
List.fill(dims._1 - 1)((0, 0, 0, 1))
}

def readRow2Check(dims: (Int,Int)): List[(Int,Int,Int,Int)] = {
// readEn, dataIn, readRow, expected dataOut
List.fill(dims._1 - 1)(( 1, 1, 1, 0)) ++
List.fill(dims._1 - 1)((0, 0, 1, 1)) ++
List.fill(dims._1 - 1)((0, 0, 1, 1))
}

def readMatrix(dims: (Int,Int)): List[(Int,Int,Int,Int)] = {
val m = genMatrix(dims)
val input = m.zipWithIndex.map{ case(row, rowIdx) =>
row.zipWithIndex.map{ case(a, colIdx) =>
// readEn, dataIn, readRow, expected dataOut
( 1, a, rowIdx, 0)
}
}.flatten

val output = m.zipWithIndex.map{ case(row, rowIdx) =>
row.zipWithIndex.map{ case(a, colIdx) =>
// readEn, dataIn, readRow, expected dataOut
( 0, 0, rowIdx, a)
}
}.flatten

input ++ output
}


it should "work like a regular daisyVec when row select is fixed to 0" in {
iotesters.Driver.execute(() => new daisyGrid(5, 4, 32), new TesterOptionsManager) { c =>
new daisyGridTest(c, readRowCheck((5,4)))
} should be(true)
}


it should "work like a regular daisyVec when row select is fixed to 1" in {
iotesters.Driver.execute(() => new daisyGrid(5, 4, 32), new TesterOptionsManager) { c =>
new daisyGridTest(c, readRow2Check((5,4)))
} should be(true)
}


it should "be able to read a matrix" in {
iotesters.Driver.execute(() => new daisyGrid(5, 4, 32), new TesterOptionsManager) { c =>
new daisyGridTest(c, readMatrix((5,4)))
} should be(true)
}
}


class daisyVecMatSpec extends FlatSpec with Matchers {
type Matrix[A] = List[List[A]]
def genMatrix(dims: (Int,Int)): Matrix[Int] =
List.fill(dims._1)(
List.fill(dims._2)(scala.util.Random.nextInt(4))
)


def generateInputs(dims: (Int,Int)): List[(Int,Int,Int,Int,Option[Int],Int,Int)] = {

val matrixB = genMatrix(dims)
val vecA = genMatrix((1, (dims._1))).head

println("multiplying: ")
println(vecA.mkString("[","\t","]"))
println("matrix:")
matrixB.foreach { row =>
println(row.mkString("[","\t","]"))
}

def answers: List[Int] = matrixB.transpose.map( col =>
(col, vecA).zipped.map(_*_).sum
)

println("should equal")
println(answers.mkString("[","\t","]"))


val vecAndMatrixInput = (matrixB.head zip vecA).map{
case(m, v) =>
(v, 1, m, 1, None, 0, 0)
}

val matrixInput = matrixB.tail.flatten.map{ m =>
(0, 0, m, 1, None, 0, 0)
}

val checkOutput = answers.map( a =>
{
val filler = List.fill(dims._2 - 1)((0, 0, 0, 0, None, 0, 0))
val check = List((0, 0, 0, 0, Some(a), 0, 0))
filler ++ check
}).flatten

vecAndMatrixInput ::: matrixInput ::: checkOutput
}



behavior of "vec mat multiplier"

it should "compile" in {
iotesters.Driver.execute(() => new daisyVecMat(5, 4, 5, 32), new TesterOptionsManager) { c =>
new daisyVecMatTest(c, Nil)
} should be(true)
}



it should "Not assert valid output when loading data" in {
iotesters.Driver.execute(() => new daisyVecMat(5, 4, 5, 32), new TesterOptionsManager) { c =>
new daisyVecMatTest(c, generateInputs((5,4)))
} should be(true)
}
}

Loading…
Cancel
Save