| @@ -0,0 +1,51 @@ | |||||
| \documentclass{article} | |||||
| \usepackage[utf8]{inputenc} | |||||
| \usepackage{tikz-timing} | |||||
| \usetikztiminglibrary[rising arrows]{clockarrows} | |||||
| \usepackage{xparse} % NewDocumentCommand, IfValueTF, IFBooleanTF | |||||
| % Reference a bus. | |||||
| % | |||||
| % Usage: | |||||
| % | |||||
| % \busref[3::0]{C/BE} -> C/BE[3::0] | |||||
| % \busref*{AD} -> AD# | |||||
| % \busref*[3::0]{C/BE} -> C/BE[3::0]# | |||||
| % | |||||
| \NewDocumentCommand{\busref}{som}{\texttt{% | |||||
| #3% | |||||
| \IfValueTF{#2}{[#2]}{}% | |||||
| \IfBooleanTF{#1}{\#}{}% | |||||
| }} | |||||
| \title{Waveform drawing} | |||||
| \author{peteraa } | |||||
| \date{June 2019} | |||||
| \begin{document} | |||||
| \begin{tikztimingtable}[% | |||||
| timing/dslope=0.1, | |||||
| timing/.style={x=5ex,y=2ex}, | |||||
| x=5ex, | |||||
| timing/rowdist=3ex, | |||||
| timing/name/.style={font=\sffamily\scriptsize} | |||||
| ] | |||||
| \busref{CLK} & 32{c} \\ | |||||
| \busref{A} & 1D{5} 1D{0} 1D{4} 1D{8} 1D{1} 1D{6} 1D{6} 1D{2} 1D{9} 1D{0} 1D{8} 1D{3} 1D{9} 1D{3} 2U \\ | |||||
| \busref{B} & 1D{8} 1D{3} 1D{3} 1D{2} 1D{7} 1D{6} 1D{7} 1D{8} 1D{5} 1D{2} 1D{7} 1D{1} 1D{8} 1D{2} 2U \\ | |||||
| \busref{VALID} & 6U 1H 6U 1H 2U\\ | |||||
| \busref{Output} & 1D{40} 1D{40} 1D{52} 1D{68} 1D{75} 1D{111} 1D{153} 1D{16} 1D{61} 1D{61} 1D{117} 1D{120} 1D{192} 1D{198} 2U\\ | |||||
| \extracode | |||||
| \begin{pgfonlayer}{background} | |||||
| \begin{scope}[semitransparent ,semithick] | |||||
| \vertlines[darkgray,dotted]{0.5,1.5 ,...,8.0} | |||||
| \end{scope} | |||||
| \end{pgfonlayer} | |||||
| \end{tikztimingtable} | |||||
| \maketitle | |||||
| \section{Introduction} | |||||
| \end{document} | |||||
| @@ -49,3 +49,14 @@ | |||||
| bi-directional busses. Otherwise, the value is simply outputted and | bi-directional busses. Otherwise, the value is simply outputted and | ||||
| the module receiving the output simply ignores it if it didn’t ask | the module receiving the output simply ignores it if it didn’t ask | ||||
| for anything. | for anything. | ||||
| * Jahre comments | |||||
| ** DONE Block diagrams | |||||
| ** DONE Specify timing constraints | |||||
| *** DONE For dot prod | |||||
| *** DONE -ish For mat mul | |||||
| ** DONE Make dot prod sim easier to find | |||||
| It was gone. Oops | |||||
| ** DONE Direct link to chisel docs | |||||
| ** DONE Modularize MatMul | |||||
| ** DONE Unrandomize tests | |||||
| @@ -6,7 +6,7 @@ | |||||
| drawing programs. | drawing programs. | ||||
| As an example we will use a very simple circuit: | As an example we will use a very simple circuit: | ||||
| [[./Images/inkscape.jpg]] | |||||
| [[./Images/inkscape.png]] | |||||
| This circuit has a register, and we want to see how its state evolves, thus we add a label. | This circuit has a register, and we want to see how its state evolves, thus we add a label. | ||||
| The name of the register is "Reg_A", which will be replaced by the actual value as the circuit | The name of the register is "Reg_A", which will be replaced by the actual value as the circuit | ||||
| @@ -82,7 +82,7 @@ | |||||
| ** Draw the circuit in inkscape | ** Draw the circuit in inkscape | ||||
| You can add as much detail as you want here, the only thing the parser looks for is | You can add as much detail as you want here, the only thing the parser looks for is | ||||
| text fields that are postfixed with "_field" | |||||
| text fields that are postfixed with "_field". | |||||
| The fields I would be interested in are the row and column counters, and the dot product | The fields I would be interested in are the row and column counters, and the dot product | ||||
| accumulator state. | accumulator state. | ||||
| Save the svg as | Save the svg as | ||||
| @@ -40,19 +40,59 @@ | |||||
| case class DotProdCalculator(vectorLen: Int, timeStep: Int, accumulator: Int){ | case class DotProdCalculator(vectorLen: Int, timeStep: Int, accumulator: Int){ | ||||
| def update(inputA: Int, inputB: Int): (Int, Boolean, DotProdCalculator) = { | def update(inputA: Int, inputB: Int): (Int, Boolean, DotProdCalculator) = { | ||||
| val product = inputA * inputB | val product = inputA * inputB | ||||
| if(((timeStep + 1) % vectorLen) == 0){ | |||||
| if(((timeStep + 1) % vectorLen) == 0) | |||||
| (accumulator + product, true, this.copy(timeStep = 0, accumulator = 0)) | (accumulator + product, true, this.copy(timeStep = 0, accumulator = 0)) | ||||
| else | else | ||||
| (accumulator + product, false, this.copy(timeStep = this.timeStep + 1, accumulator = accumulator + product)) | (accumulator + product, false, this.copy(timeStep = this.timeStep + 1, accumulator = accumulator + product)) | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| #+end_src | #+end_src | ||||
| To see it in action run ~testOnly Ex0.DPCsimulatorSpec~ in your sbt console. | |||||
| To see it in action run ~testOnly Examples.SoftwareDotProdSpec~ in your sbt console. | |||||
| As with the previous tasks, the dot product calculator must pass the tests with | As with the previous tasks, the dot product calculator must pass the tests with | ||||
| ~testOnly Ex0.DotProdSpec~ | ~testOnly Ex0.DotProdSpec~ | ||||
| *** Timing | |||||
| As shown in the timing diagram below, the dot product calculator should deliver the result as | |||||
| soon as possible. | |||||
| This means you will have to drive the output with the sum of the accumulator and the product of | |||||
| the two inputs. | |||||
| If you choose to drive the output only by the value of the accumulator your circuit will | |||||
| lag behind by one cycle, which while good for pipelining purposes is not good for passing the test | |||||
| purposes. | |||||
| #+CAPTION: The expected output of the dot product calculator | |||||
| [[./Images/counter.png]] | |||||
| *** Chisel counters and a short detour on scala documentation | |||||
| Doing an action for a set amount of timesteps is a very common task in hardware design, so this | |||||
| functionality is included in chisel via the Counter class. | |||||
| In order to understand how this counter works you can google "chisel counter" and get the following | |||||
| https://chisel.eecs.berkeley.edu/api/3.0.1/chisel3/util/Counter.html as first result. | |||||
| However, this is for the counter Class, what you actually want is the counter Object: | |||||
| https://chisel.eecs.berkeley.edu/api/3.0.1/chisel3/util/Counter$.html | |||||
| This can be very confusing when new to scala, but it is simply convention: | |||||
| When a class and an object share name this is just a convenience for keeping static methods, such | |||||
| as constructors, separated from the non-static methods. | |||||
| In the Counter object (the second link) there is an apply method: | |||||
| #+begin_src scala | |||||
| def apply(cond: Bool, n: Int): (UInt, Bool) | |||||
| #+end_src | |||||
| The type signature tells you that the input is a regular scala integer, and a chisel boolean | |||||
| (scala booleans are of type ~Boolean~, rather than ~Bool~) and the output is a UInt and a chisel | |||||
| boolean. | |||||
| This means that upon instantiating a Counter with its apply method you only get the outputs from | |||||
| the counter, not the object itself. | |||||
| The result is a convenient method of making a counter, simply supply how many ticks it takes for the | |||||
| counter to roll over, as well as an input signal for enabling the clock, and receive a tuple with the | |||||
| signal for the counters value, as well as a boolean signal that toggles whenever the clock rolls over. | |||||
| A special property of apply methods are that they can be called directly on the object. | |||||
| ~Counter.apply(cCond, 10)~ does the same as ~Counter(cCond, 10)~. | |||||
| To call the class constructor, use the ~new~ keyword. | |||||
| ** Task 4 - Matrix Matrix multiplication | ** Task 4 - Matrix Matrix multiplication | ||||
| @@ -106,9 +146,43 @@ | |||||
| The skeleton code for the matrix multiplier is less detailed, with only one test. | The skeleton code for the matrix multiplier is less detailed, with only one test. | ||||
| You're encouraged to write your own tests to make this easier. | You're encouraged to write your own tests to make this easier. | ||||
| Additionally, if you feel like you're getting stuck you can take a look at | |||||
| MatMulTips.org | |||||
| *** Structuring your circuit | |||||
| It is very easy to get bogged down with details in this exercise, so it's useful to take | |||||
| a few moments to plan ahead. | |||||
| A natural way to break down the task is to split it into two phases: setup and execution. | |||||
| For setup you simply want to shuffle data from the input signals to your two matrix modules. | |||||
| The next task is to actually perform the calculation. | |||||
| This is a little more complex, seeing as the read patterns are different from matrix A and B. | |||||
| To make this simpler a good idea is to introduce a control module. | |||||
| This module should keep track of which state the multiplier is in, setup or execution, and | |||||
| provide the appropriate row and column select signals. | |||||
| You may also choose to split the control module into an init controller and an execution | |||||
| controller if you see fit. | |||||
| A suggested design is shown underneath: | |||||
| [[./Images/MatMul.png]] | |||||
| *** Timing | |||||
| The timing for your matrix multiplier is straight forward. For a 3x4 matrix it takes | |||||
| 12 cycles to input data (cycles 0 to 11), and execution should proceed on cycle 12. | |||||
| While you can technically start execution sooner than this the tests expect you to | |||||
| not start executing before all data is loaded. | |||||
| As long as you start executing just as data has been loaded your dot prod design will | |||||
| take care of the rest. | |||||
| *** Testing | |||||
| In order to make testing easier, consider testing your row and column select signals | |||||
| first. | |||||
| The actual values stored in the matrixes are just noise, the important part is that | |||||
| you select the correct rows and columns at the correct times for the correct matrixes, | |||||
| and if you do this the rest is comparatively easy. | |||||
| ** Bonus exercise - Introspection on code quality and design choices | ** Bonus exercise - Introspection on code quality and design choices | ||||
| This last exercise has no deliverable, but you should spend some time thinking about | This last exercise has no deliverable, but you should spend some time thinking about | ||||
| where you spent most of your efforts. | where you spent most of your efforts. | ||||
| @@ -852,9 +852,18 @@ | |||||
| Just don't bank your money on the correctness, it might fail in rare circumstances making debugging | Just don't bank your money on the correctness, it might fail in rare circumstances making debugging | ||||
| a nightmare) | a nightmare) | ||||
| ** Visualizing generated circuits | |||||
| While limited, it is possible to visualize your generated circuit using [[https://github.com/freechipsproject/diagrammer][diagrammer]]. | |||||
| The necessary code to generate .fir file is in the main.scala file, just comment it out to generate | |||||
| these. | |||||
| I encourage you to give it a fair shake to see if you find it useful or not. | |||||
| ** Visualizing circuit state (Optional) | ** Visualizing circuit state (Optional) | ||||
| In order to make debugging easier it is helpful to render the state of the circuit to see where | In order to make debugging easier it is helpful to render the state of the circuit to see where | ||||
| errors happen. | errors happen. | ||||
| A prototype for this is included in this project, read more about it here | A prototype for this is included in this project, read more about it here | ||||
| [[./circuitRendering.org][Here]] | [[./circuitRendering.org][Here]] | ||||
| ** Resources | |||||
| Chisel cheat sheet | |||||
| https://chisel.eecs.berkeley.edu/doc/chisel-cheatsheet3.pdf | |||||
| @@ -21,6 +21,8 @@ class DotProd(val elements: Int) extends Module { | |||||
| */ | */ | ||||
| val counter = Counter(elements) | val counter = Counter(elements) | ||||
| val accumulator = RegInit(UInt(32.W), 0.U) | val accumulator = RegInit(UInt(32.W), 0.U) | ||||
| // Please don't manually implement product! | |||||
| val product = io.dataInA * io.dataInB | val product = io.dataInA * io.dataInB | ||||
| // placeholder | // placeholder | ||||
| @@ -1,12 +1,22 @@ | |||||
| package Ex0 | package Ex0 | ||||
| import chisel3._ | |||||
| import java.io.File | |||||
| object main { | object main { | ||||
| def main(args: Array[String]): Unit = { | def main(args: Array[String]): Unit = { | ||||
| val s = """ | val s = """ | ||||
| | Attempting to "run" a chisel program is rather meaningless. | | Attempting to "run" a chisel program is rather meaningless. | ||||
| | Instead, try running the tests, for instance with "test" or "testOnly Examples.MyIncrementTest | | Instead, try running the tests, for instance with "test" or "testOnly Examples.MyIncrementTest | ||||
| | | |||||
| | If you want to create chisel graphs, simply remove this message and comment in the code underneath | |||||
| | to generate the modules you're interested in. | |||||
| """.stripMargin | """.stripMargin | ||||
| println(s) | println(s) | ||||
| } | } | ||||
| // Uncomment to dump .fir file | |||||
| // val f = new File("MatMul.fir") | |||||
| // chisel3.Driver.dumpFirrtl(chisel3.Driver.elaborate(() => new MatMul(5, 4)), Option(f)) | |||||
| } | } | ||||
| @@ -8,7 +8,8 @@ import TestUtils._ | |||||
| class DotProdSpec extends FlatSpec with Matchers { | class DotProdSpec extends FlatSpec with Matchers { | ||||
| import DotProdTests._ | import DotProdTests._ | ||||
| val elements = scala.util.Random.nextInt(5) + 2 | |||||
| val rand = new scala.util.Random(100) | |||||
| val elements = 7 | |||||
| behavior of "DotProd" | behavior of "DotProd" | ||||
| @@ -41,6 +42,8 @@ class DotProdSpec extends FlatSpec with Matchers { | |||||
| object DotProdTests { | object DotProdTests { | ||||
| val rand = new scala.util.Random(100) | |||||
| class SignalsWhenDone(c: DotProd) extends PeekPokeTester(c) { | class SignalsWhenDone(c: DotProd) extends PeekPokeTester(c) { | ||||
| for(ii <- 0 until c.elements - 1){ | for(ii <- 0 until c.elements - 1){ | ||||
| @@ -61,8 +64,11 @@ object DotProdTests { | |||||
| class CalculatesCorrectResult(c: DotProd) extends PeekPokeTester(c) { | class CalculatesCorrectResult(c: DotProd) extends PeekPokeTester(c) { | ||||
| val inputsA = List.fill(c.elements)(scala.util.Random.nextInt(10)) | |||||
| val inputsB = List.fill(c.elements)(scala.util.Random.nextInt(10)) | |||||
| val inputsA = List.fill(c.elements)(rand.nextInt(10)) | |||||
| val inputsB = List.fill(c.elements)(rand.nextInt(10)) | |||||
| println("runnign dot prod calc with inputs:") | |||||
| println(inputsA.mkString("[", "] [", "]")) | |||||
| println(inputsB.mkString("[", "] [", "]")) | |||||
| val expectedOutput = (for ((a, b) <- inputsA zip inputsB) yield a * b) sum | val expectedOutput = (for ((a, b) <- inputsA zip inputsB) yield a * b) sum | ||||
| for(ii <- 0 until c.elements){ | for(ii <- 0 until c.elements){ | ||||
| @@ -77,8 +83,11 @@ object DotProdTests { | |||||
| class CalculatesCorrectResultAndSignals(c: DotProd) extends PeekPokeTester(c) { | class CalculatesCorrectResultAndSignals(c: DotProd) extends PeekPokeTester(c) { | ||||
| val inputsA = List.fill(c.elements)(scala.util.Random.nextInt(10)) | |||||
| val inputsB = List.fill(c.elements)(scala.util.Random.nextInt(10)) | |||||
| val inputsA = List.fill(c.elements)(rand.nextInt(10)) | |||||
| val inputsB = List.fill(c.elements)(rand.nextInt(10)) | |||||
| println("runnign dot prod calc with inputs:") | |||||
| println(inputsA.mkString("[", "] [", "]")) | |||||
| println(inputsB.mkString("[", "] [", "]")) | |||||
| val expectedOutput = (for ((a, b) <- inputsA zip inputsB) yield a * b) sum | val expectedOutput = (for ((a, b) <- inputsA zip inputsB) yield a * b) sum | ||||
| for(ii <- 0 until c.elements){ | for(ii <- 0 until c.elements){ | ||||
| @@ -0,0 +1,42 @@ | |||||
| /** | |||||
| * This code supplements instructions.org | |||||
| */ | |||||
| package Examples | |||||
| import Ex0._ | |||||
| import org.scalatest.{Matchers, FlatSpec} | |||||
| case class DotProdCalculator(vectorLen: Int, timeStep: Int = 0, accumulator: Int = 0){ | |||||
| def update(inputA: Int, inputB: Int): (Int, Boolean, DotProdCalculator) = { | |||||
| val product = inputA * inputB | |||||
| if(((timeStep + 1) % vectorLen) == 0) | |||||
| (accumulator + product, true, this.copy(timeStep = 0, accumulator = 0)) | |||||
| else | |||||
| (accumulator + product, false, this.copy(timeStep = this.timeStep + 1, accumulator = accumulator + product)) | |||||
| } | |||||
| } | |||||
| class SoftwareDotProdSpec extends FlatSpec with Matchers { | |||||
| import DotProdTests._ | |||||
| val elements = scala.util.Random.nextInt(5) + 2 | |||||
| behavior of "DotProdSim" | |||||
| it should "Simulate dot product calculation" in { | |||||
| println("Running a simulated dot product calculator with input vector size = 5") | |||||
| println("Note how output is only valid on cycles divisible by 5, and how the accumulator flushes\n\n") | |||||
| ((0 to 15).map(_ => util.Random.nextInt(8)) zip | |||||
| (0 to 15).map(_ => util.Random.nextInt(8))) | |||||
| .foldLeft(DotProdCalculator(5)){ case(dotProd, (inputA, inputB)) => | |||||
| val (output, valid, nextDotProd) = dotProd.update(inputA, inputB) | |||||
| val inputString = s"At timestep ${dotProd.timeStep} inputs A: " + Console.YELLOW + s"$inputA " + Console.RESET + "B: " + Console.YELLOW + s"$inputB" + Console.RESET | |||||
| val outputString = s"the output was: output: " + Console.YELLOW + s"$output" + Console.RESET + ", valid: " + (if(valid) Console.GREEN else Console.RED) + s"$valid\n" + Console.RESET | |||||
| println(inputString) | |||||
| println(outputString) | |||||
| nextDotProd | |||||
| } | |||||
| true | |||||
| } | |||||
| } | |||||
| @@ -8,8 +8,8 @@ import TestUtils._ | |||||
| class MatMulSpec extends FlatSpec with Matchers { | class MatMulSpec extends FlatSpec with Matchers { | ||||
| import MatMulTests._ | import MatMulTests._ | ||||
| val rowDims = scala.util.Random.nextInt(5) + 3 | |||||
| val colDims = scala.util.Random.nextInt(5) + 3 | |||||
| val rowDims = 3 | |||||
| val colDims = 7 | |||||
| behavior of "MatMul" | behavior of "MatMul" | ||||
| @@ -25,6 +25,8 @@ class MatMulSpec extends FlatSpec with Matchers { | |||||
| object MatMulTests { | object MatMulTests { | ||||
| val rand = new scala.util.Random(100) | |||||
| class TestExample(c: MatMul) extends PeekPokeTester(c) { | class TestExample(c: MatMul) extends PeekPokeTester(c) { | ||||
| val mA = genMatrix(c.rowDimsA, c.colDimsA) | val mA = genMatrix(c.rowDimsA, c.colDimsA) | ||||
| val mB = genMatrix(c.rowDimsA, c.colDimsA) | val mB = genMatrix(c.rowDimsA, c.colDimsA) | ||||
| @@ -1,58 +0,0 @@ | |||||
| The most important part of this task is keeping track of which row and column should be | |||||
| accessed in each matrix. | |||||
| In short, there are three values you need to have control over: | |||||
| Column select, Row select A and Row select B | |||||
| Further, it is useful to separate input phase and calculation phase. | |||||
| In the input phase Row select A should be the same as Row select B since | |||||
| you're simply inputting values. | |||||
| For a 3x2 matrix you want to first input the first vector (that is, row 0), | |||||
| so for cycle 0, 1 and 2 rowSelect should be 0, whereas column select should be | |||||
| the same as the (cycle % columns) | |||||
| After Inputting data all three values should be reset to 0. | |||||
| This is a fairly typical task, so chisel.util has you covered with the Counter class | |||||
| https://chisel.eecs.berkeley.edu/api/3.0.1/chisel3/util/Counter.html | |||||
| https://chisel.eecs.berkeley.edu/api/3.0.1/chisel3/util/Counter$.html | |||||
| The second link links to the Counter object rather than the class. Typically we put | |||||
| constructors and static methods in the companion object of a class. | |||||
| Object methods named apply can be called directly from the object, which means | |||||
| ~MyObject.apply(123)~ is the same as ~MyObject(123)~ | |||||
| Very convenient once you're used to it, but a little odd first time you see it. | |||||
| In the Counter object there is an apply method: | |||||
| #+begin_src scala | |||||
| def apply(cond: Bool, n: Int): (UInt, Bool) | |||||
| #+end_src | |||||
| From the signature we see (well, I see because I happen to know that scala bools are called Boolean) | |||||
| that the input is a chisel.data.Bool and a scala Int, and the output is a tuple of | |||||
| chisel.data.UInt and chisel.data.Bool | |||||
| The return values for the call to Counter.apply is a wire containing the current value of the counter, | |||||
| and a wire that is toggled whenever the clock rolls over. | |||||
| The arguments are a scala int, specifying how many ticks it takes for the clock to roll over, and a | |||||
| wire whose signal toggles the clock on or off. | |||||
| In our matrix multiplier this is pretty handy: | |||||
| #+begin_src scala | |||||
| val (colCounter, colCounterWrap) = Counter(true.B, colDimsA) | |||||
| val (rowSelA, rowSelAWrap) = Counter(colCounterWrap, ???) | |||||
| #+end_src | |||||
| Here we have defined two counters. The column counter always ticks, wrapping around when it reaches colDimsA. | |||||
| When the column counter wraps, the colCounterWrap wire is toggled, and the rowSelect clock makes a single tick. | |||||
| If you can get row select A, B and col select to work in both phases you're very close to done, so these | |||||
| should be your priority. | |||||
| To test them, write your own test for MatMul that simply runs your MatMul unit and peeks at these values each | |||||
| timestep until you're confident that they're correct. | |||||
| As described in the debugging section, you should make a separate debug IO port with debug signals enabling you | |||||
| to read out these values. Attempting to read them directly will throw an error. | |||||
| The best tip of course is to actually show up during lab hours. | |||||
| @@ -12,12 +12,22 @@ class MatrixSpec extends FlatSpec with Matchers { | |||||
| behavior of "Matrix" | behavior of "Matrix" | ||||
| val rowDims = scala.util.Random.nextInt(5) + 3 | |||||
| val colDims = scala.util.Random.nextInt(5) + 3 | |||||
| val rand = new scala.util.Random(100) | |||||
| val rowDims = 5 | |||||
| val colDims = 3 | |||||
| it should "Update its contents" in { | |||||
| it should "Update its contents with a square shape" in { | |||||
| wrapTester( | wrapTester( | ||||
| chisel3.iotesters.Driver(() => new Matrix(10,10)) { c => | |||||
| chisel3.iotesters.Driver(() => new Matrix(rowDims,rowDims)) { c => | |||||
| new UpdatesData(c) | |||||
| } should be(true) | |||||
| ) | |||||
| } | |||||
| it should "Update its contents with a rectangular shape" in { | |||||
| wrapTester( | |||||
| chisel3.iotesters.Driver(() => new Matrix(rowDims,colDims)) { c => | |||||
| new UpdatesData(c) | new UpdatesData(c) | ||||
| } should be(true) | } should be(true) | ||||
| ) | ) | ||||
| @@ -26,7 +36,7 @@ class MatrixSpec extends FlatSpec with Matchers { | |||||
| it should "Retain its contents when writeEnable is low" in { | it should "Retain its contents when writeEnable is low" in { | ||||
| wrapTester( | wrapTester( | ||||
| chisel3.iotesters.Driver(() => new Matrix(10,10)) { c => | |||||
| chisel3.iotesters.Driver(() => new Matrix(rowDims, colDims)) { c => | |||||
| new UpdatesData(c) | new UpdatesData(c) | ||||
| } should be(true) | } should be(true) | ||||
| ) | ) | ||||
| @@ -35,10 +45,12 @@ class MatrixSpec extends FlatSpec with Matchers { | |||||
| object MatrixTests { | object MatrixTests { | ||||
| val rand = new scala.util.Random(100) | |||||
| class UpdatesData(c: Matrix) extends PeekPokeTester(c) { | class UpdatesData(c: Matrix) extends PeekPokeTester(c) { | ||||
| val inputs = List.fill(c.colsDim){ | val inputs = List.fill(c.colsDim){ | ||||
| List.fill(c.rowsDim)(scala.util.Random.nextInt(20) + 1) | |||||
| List.fill(c.rowsDim)(rand.nextInt(20) + 1) | |||||
| } | } | ||||
| poke(c.io.writeEnable, true) | poke(c.io.writeEnable, true) | ||||
| @@ -65,7 +77,7 @@ object MatrixTests { | |||||
| class RetainsData(c: Matrix) extends PeekPokeTester(c) { | class RetainsData(c: Matrix) extends PeekPokeTester(c) { | ||||
| val inputs = List.fill(c.colsDim){ | val inputs = List.fill(c.colsDim){ | ||||
| List.fill(c.rowsDim)(scala.util.Random.nextInt(20) + 1) | |||||
| List.fill(c.rowsDim)(rand.nextInt(20) + 1) | |||||
| } | } | ||||
| poke(c.io.writeEnable, true) | poke(c.io.writeEnable, true) | ||||
| @@ -7,8 +7,10 @@ import org.scalatest.{Matchers, FlatSpec} | |||||
| object TestUtils { | object TestUtils { | ||||
| val rand = new scala.util.Random(100) | |||||
| def genMatrix(rows: Int, cols: Int) = List.fill(rows)( | def genMatrix(rows: Int, cols: Int) = List.fill(rows)( | ||||
| List.fill(cols)(scala.util.Random.nextInt(5)) | |||||
| List.fill(cols)(rand.nextInt(5)) | |||||
| ) | ) | ||||
| def printVector(v: List[Int]): String = | def printVector(v: List[Int]): String = | ||||
| @@ -40,6 +42,30 @@ object TestUtils { | |||||
| println("##########################################################") | println("##########################################################") | ||||
| println("##########################################################") | println("##########################################################") | ||||
| } | } | ||||
| case e: chisel3.core.Binding.ExpectedHardwareException => { | |||||
| println("##########################################################") | |||||
| println("##########################################################") | |||||
| println("##########################################################") | |||||
| println("Your design is using raw chisel types!") | |||||
| println("error:\n") | |||||
| println(e.getMessage) | |||||
| println("") | |||||
| println("") | |||||
| println("This typically occurs when you forget to wrap a module") | |||||
| println("e.g") | |||||
| println(""" | |||||
| class MyBundle extends Bundle { | |||||
| val signal = UInt(32.W) | |||||
| } | |||||
| val mySignal = new MyBundle | |||||
| ^^^^ Wrong! | |||||
| should be | |||||
| val mySignal = Wire(new MyBundle) | |||||
| """) | |||||
| println("##########################################################") | |||||
| println("##########################################################") | |||||
| println("##########################################################") | |||||
| } | |||||
| case e: Exception => throw e | case e: Exception => throw e | ||||
| } | } | ||||
| } | } | ||||
| @@ -11,7 +11,7 @@ import scala.collection.immutable.{ Vector => _ } | |||||
| class VectorSpec extends FlatSpec with Matchers { | class VectorSpec extends FlatSpec with Matchers { | ||||
| import VectorTests._ | import VectorTests._ | ||||
| val elements = scala.util.Random.nextInt(5) + 2 | |||||
| val elements = 7 | |||||
| behavior of "Vector" | behavior of "Vector" | ||||