Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions docs/src/cookbooks/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,71 @@ 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.

```scala
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)"
}
Comment thread
MrAMS marked this conversation as resolved.

// 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:

```scala
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.:
Expand Down
6 changes: 1 addition & 5 deletions docs/src/explanations/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,10 @@ These APIs are summarized below:

- `poke` sets a value on a port
- `peek` reads a value on a port
- `expect` reads a value on a port and asserts that it is equal another value
- `expect` reads a value on a port and asserts that it is equal to another value, with optional custom formatting (see the [testing cookbook](../cookbooks/testing#how-do-i-customize-expect-failure-value-formatting))
- `step` toggles a clock for a number of cycles
- `stepUntil` toggles a clock until a condition occurs on another port

For more information see the [Chisel API
documentation](https://www.chisel-lang.org/api) for
`chisel3.simulator.PeekPokeAPI`.

#### Reusable Stimulus Patterns

While the Peek/Poke APIs are useful for freeform tests, there are a number of
Expand Down
Loading
Loading