Skip to main content

Testing Cookbook

How do I change the default testing directory?

Override the buildDir method.

The example below changes the testing directory to test/:

import chisel3._
import chisel3.simulator.scalatest.ChiselSim
import java.nio.file.Paths
import org.scalatest.funspec.AnyFunSpec

class FooSpec extends FunSpec with ChiselSim {

override def buildDir: Path = Paths.get("test")

}

How do I enable waveforms for a simulation?

If using Scalatest and ChiselSim, pass the -DemitVcd=1 argument to Scalatest, e.g.:

./mill 'chisel[].test.testOnly' chiselTests.ShiftRegistersSpec -- -DemitVcd=1

How do I enable Verilator coverage in svsim?

If you are configuring svsim directly, set Verilator backend coverage settings at compile time:

import svsim.verilator

val backend = verilator.Backend.initializeFromProcessEnvironment()
val settings = verilator.Backend.CompilationSettings.default
.withCoverageSettings(
new verilator.Backend.CompilationSettings.CoverageSettings(
line = true,
toggle = true,
user = true
)
)

This enables Verilator coverage instrumentation and writes coverage.dat at the end of simulation. You can convert it to LCOV info with verilator_coverage for downstream reporting tools.

How do I customize expect failure value formatting?

Pass a format directly to expect. Hex groups pairs of digits by byte, and Bin groups bits in fours.

import chisel3.simulator.ExpectationValueFormat

simulate(new Foo) { dut =>
// Render failures value in hexadecimal
dut.io.out.expect(0xfe.U, ExpectationValueFormat.Hex)

// Render failures value in binary
dut.io.out.expect(0xff.U, ExpectationValueFormat.Bin)

val jumpInst = BigInt("0000006f", 16) // jal x0, 0
val retInst = BigInt("00008067", 16) // jalr x0, x1, 0 (ret)

// A custom formatter can inspect the raw bits through `value.unsignedValue` and
// render a domain-specific label without changing the comparison semantics.
val custom = ExpectationValueFormat.Custom { value =>
val mnemonic = value.unsignedValue match {
case `jumpInst` => "jump"
case `retInst` => "ret"
case inst => s"unknown(0x${inst.toString(16)})"
}
s"riscv($mnemonic)"
}

// This still checks the exact value; only the failure message rendering is customized.
dut.io.out.expect(jumpInst.U(32.W), custom)
}

You can also customize the failure message separately from the rendered values:

import chisel3.simulator.ExpectationValueFormat

def bits(value: ExpectationValueFormat.Value): String =
value.unsignedValue.toString(2).reverse.padTo(value.bitWidth, '0').reverse.mkString

val bitDiff = ExpectationValueFormat.Custom.message(
ExpectationValueFormat.Custom(bits)
) { (observed, expected) =>
val observedBits = bits(observed)
val expectedBits = bits(expected)
val markers = observedBits.zip(expectedBits).map {
case (observedBit, expectedBit) => if (observedBit == expectedBit) ' ' else '^'
}.mkString
val diffBits = observedBits.zip(expectedBits).zipWithIndex.collect {
case ((observedBit, expectedBit), idx) if observedBit != expectedBit =>
observedBits.length - 1 - idx
}.sorted
val indent = " " * "Observed value: '".length

s"""|$indent$markers
|Diff Bit: ${diffBits.mkString(",")}""".stripMargin
}

simulate(new Foo) { dut =>
dut.io.in.poke("b101111".U)
dut.io.out.expect("b100101".U, bitDiff)
}

How do I see what options a ChiselSim Scalatest test supports?

Pass -Dhelp=1 to Scalatest, e.g.:

./mill 'chisel[].test.testOnly' chiselTests.ShiftRegistersSpec -- -Dhelp=1