Monday, May 8, 2017

How to test a parameterized module in Chisel

Quick update today.  Busy with real work.  This quick post is written because how to test these modules in not adequately covered in the documentation for chisel-testers.

Consider the case for a parameterizable LSFR module.


class LSFR[T <: Bundle](gen: T = UInt(4.W)) extends Module {
  val io = IO(new Bundle {
    val rand = Output(gen)
  })
  val width = gen.getWidth
  val regLFSR = Reg(UInt(width.W), init = UInt(0))
  when(true.B) {
    regLFSR := Cat(regLFSR(0) ^ regLFSR(1), regLFSR(width - 1, 1))
  }
  io.rand := gen.fromBits(regLFSR)
}

Below I show a test containing two examples.  First using PokeTester and the second using PeekPokeTester.  See if you can spot the difference ...


class LSFRPeekPokeTester[T <: Bundle](gen: T, c: LSFR[T]) extends PeekPokeTester(c) {
  println("peekpoke test")
}

class LSFRSpec extends ChiselFlatSpec with PokeTester {
  behavior of "LSFR"

  it should "elaborate with peek test" in {
    val options = new TesterOptionsManager
    options.setTargetDirName("test_run_dir/LSFRSpec")
    test(new LSFR(new DDRCommand), testerBackend = FirrtlInterpreterBackend, options = options) {
      (t, c) => {
        println("poke test")
      }
    }
  }

  it should "elaborate with peekpoke test" in {
    Driver(() => new LSFR(new DDRCommand)) {
      c => new LSFRPeekPokeTester(new DDRCommand, c)
    }
  }
}

In the PokeTester case, the first argument to test is new LSFR(new DDRCommand).  For PeekPokeTester, you pass a function to generate the module () => new LSFR(new DDRCommand).  Not immediately obvious.  Now, look at the function prototypes for PokeTester and PeekPokeTester respectively ...


// PokeTester source
def test[T <: Module](dutGen: => T, testerBackend: TesterBackend, options: TesterOptionsManager)(block: (InnerTester, T) => Unit) { //[..]

// PeekPokeTester source
def apply[T <: Module](
      dutGen: () => T,
      backendType: String = "firrtl",
      verbose: Boolean = false,
      testerSeed: Long = System.currentTimeMillis())(
      testerGen: T => PeekPokeTester[T]): Boolean = { //[..]

The definition of dutGen is different.

Wise to keep in mind that PokeTester is under iotesters.experimental.