4

I'm a fresher in using Chisel3 to build the hardware components. Now, I'm trying to make a small module in which there are 2 inputs (vector A and vector B) and 1 outputs (out with 1 bits Unsigned Integer). Out is a Dot Product of A and B.

I declared the module DotProduct to make the calculation. Here is my code:

class RealDotProduct extends Module {
  val io = IO(new Bundle {
    val vectorA = Input(Vec(2, UInt(2.W)))
    val vectorB = Input(Vec(2, UInt(2.W)))
    val out  = Output(UInt(1.W))
  })

  val product = 0.U

  //Loop to calculate the inner (dot) product
  for (i <- 0 until 2) {
      product := product + io.vectorA(i) * io.vectorB(i)
  }
  io.out := product
}

And, here is my simple test

class RealDotProductTests(c: RealDotProduct) extends PeekPokeTester(c) {
  import scala.collection.immutable._  
  val in_a = Vector(0,1)
  val in_b = Vector(2,3)

  for (i <- 0 until 2) {
      poke (c.io.vectorA(i), in_a(i))
      poke (c.io.vectorB(i), in_b(i))
  }
  step(1)
  expect(c.io.out, 3)
}

class RealDotProductTester extends ChiselFlatSpec {
  behavior of "RealDotProduct"
  backends foreach {backend =>
    it should s"perform correct math operation on dynamic operand in $backend" in {
      Driver(() => new RealDotProduct, backend)((c) => new RealDotProductTests(c)) should be (true)
    }
  }
}
object RealDotProductMain extends App {
  iotesters.Driver.execute(args, () => new RealDotProduct) {
    c => new RealDotProductTests(c)
  }
}

I run the test using sbt and I got the error log:

[info] [0.001] Elaborating design...
[error] chisel3.internal.ChiselException: Cannot reassign to read-only UInt<1>(0)
[error]         ...
[error]         at real_dot_product.RealDotProduct.$anonfun$new$1(RealDotProduct.scala:22)
[error]         at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:158)
[error]         at real_dot_product.RealDotProduct.<init>(RealDotProduct.scala:21)
[error]         at real_dot_product.RealDotProductMain$.$anonfun$new$1(RealDotProductMain.scala:27)
[error] ychiselp... (Stack trace trimmed to user code only, rerun with --full-stacktrace if you wish to see the full stack trace)
[error] (run-main-0) java.lang.Exception: Failed to elaborate Chisel circuit
[error] java.lang.Exception: Failed to elaborate Chisel circuit
[error]     at chisel3.iotesters.setupVerilatorBackend$.apply(VerilatorBackend.scala:286)
[error]     at chisel3.iotesters.Driver$.$anonfun$execute$2(Driver.scala:55)
  • Question 1: Could you please show me where is the problem in my coding?
  • Question 2: What should I replace 3 in expect(c.io.out,3) in case I want to run many test with random value for vectorA and vector B?

Thanks a lot guys.

dave_59
  • 39,096
  • 3
  • 24
  • 63
nhvlong06
  • 41
  • 1

1 Answers1

2

Answer1: Chisel is assembling a graph of connections representing the hardware you want. One of the problems you have is that you are connecting things multiple times to the wire product. That can make sense in software but not so much in hardware.

If you were to describe what you wanted to do verbally, it would probably be something like, "pair up each element from inA and inB and multiply them together, then add up the sum of those products. In Chisel one way to do this is like this.

val products = io.vectorA.zip(io.vectorB).map { case (a, b) => a * b }
val sum = product.reduce { case (a, b) => a + b }
io,out := sum

The zip pairs up the elements of your two input vecs. The map converts each pair of values to a sequence of products. You can then sum up those products with reduce.

You also have a bit of a problem with widths in your IO ports. Your out port does not have enough bits to contain the inner product of the two vectors. Chisel can handle some of the work about how many bits you need for your computation, but for IO ports you still need to think carefully about how many bits you want.

Very soon, as you become more comfortable with the scala idioms for working with collections (things like, Vec, Seq) you will be able write this kind of module even more succinctly, something like:

  io.out := io.vectorA.zip(io.vectorB).map(_ * _).reduce(_ + _)

Answer2: Just write a function in your tester to compute the value. The basic way to right it would be something like

var sum = 0 for(i <- in_a.indices) { sum += (in_a(i) * in_b(i)) }

... expect(c.io.out, sum)

You could do something fancier. In regards to the bit width of the out. It should probably be big enough to handle the largest value you expect from the computation for arbitrary inputs.

Chick Markley
  • 4,023
  • 16
  • 17