Skip to content
Open
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
42 changes: 42 additions & 0 deletions docs/src/appendix/migrating-from-chiseltest.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,45 @@ println("`"*3)
<!-- KEEP THE CODE BLOCKS ABOVE IN SYNC -->

ChiselSim also does not currently have any support for `fork`-`join`, so any tests using those constructs will need to be rewritten in a single-threaded manner.

## ChiselTest Compatibility Layer

For projects with a large test suite, Chisel now includes a **ChiselTest compatibility layer** at `chiseltest._` that provides a drop-in replacement for most ChiselTest APIs. This allows you to keep your existing test code while running on ChiselSim.

### Using the Compatibility Layer

Simply keep your existing imports and test structure:

```scala mdoc
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec

class MyModuleSpec extends AnyFlatSpec with ChiselScalatestTester {
behavior of "MyModule"
it should "do something" in {
test(new MyModule) { c =>
c.io.in.poke(42.U)
c.clock.step()
c.io.out.expect(42.U)
}
}
}
```

The compatibility layer provides:
- `poke`, `peek`, `expect` methods on Data types
- `step()` methods on Clock
- `ChiselScalatestTester` trait for ScalaTest integration
- Automatic reset before each test (mimicking ChiselTest behavior)
- Decoupled interface utilities (`enqueueNow`, `expectDequeueNow`, etc.)
- Annotation stubs (`WriteVcdAnnotation`, etc.) for compatibility

### Limitations

- Annotations are ignored (waveforms always generated with Verilator)
- Formal verification support is limited to stubs
- `fork`/`join` patterns execute sequentially
- Some advanced features may require migration to ChiselSim APIs

For full details, see `src/main/scala/chiseltest/README.md` in the Chisel repository.
191 changes: 191 additions & 0 deletions src/main/scala/chiseltest/ChiselScalatestTester.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// SPDX-License-Identifier: Apache-2.0

package chiseltest

import chisel3._
import chisel3.simulator.EphemeralSimulator._
import chisel3.simulator.ChiselSim
import svsim._
import svsim.verilator.Backend.CompilationSettings.{TraceKind, TraceStyle}
import scala.language.implicitConversions

/**
* ChiselTest-compatible API that delegates to ChiselSim (Chisel 7)
*
* This trait provides the ChiselTest API that users are familiar with from Chisel 6,
* but internally uses ChiselSim from Chisel 7 to perform the actual testing.
*
* VCD GENERATION SUPPORT:
* This compatibility layer supports VCD generation when WriteVcdAnnotation is used.
* VCD files are generated in: build/chiselsim/<timestamp>/workdir-verilator/trace.vcd
*
* AUTOMATIC RESET FEATURE:
* By default, this trait automatically resets the module before running tests,
* mimicking ChiselTest's behavior from Chisel 6.
*
* To disable auto-reset or customize the reset duration:
* {{{
* class MyTest extends AnyFlatSpec with ChiselScalatestTester {
* override def autoResetEnabled: Boolean = false // Disable auto-reset
* override def resetCycles: Int = 5 // Or change duration
* }
* }}}
*
* Example usage with VCD:
* {{{
* import chiseltest._
* import org.scalatest.flatspec.AnyFlatSpec
*
* class MyModuleSpec extends AnyFlatSpec with ChiselScalatestTester {
* behavior of "MyModule"
*
* it should "generate waveforms" in {
* test(new MyModule).withAnnotations(Seq(WriteVcdAnnotation)) { dut =>
* dut.io.in.poke(42.U)
* dut.clock.step()
* dut.io.out.expect(42.U)
* }
* // VCD file: build/chiselsim/<timestamp>/workdir-verilator/trace.vcd
* }
* }
* }}}
*/
trait ChiselScalatestTester {

/**
* Enable automatic reset before each test.
* Override this to disable auto-reset if needed.
*/
def autoResetEnabled: Boolean = false

/**
* Number of clock cycles to assert reset.
* Override this to change reset duration.
*/
def resetCycles: Int = 1

/**
* Test a module with the given stimulus
*
* This method provides the ChiselTest interface.
*
* @param dutGen A generator function that creates the device under test
* @tparam T The type of module being tested
*/
def test[T <: Module](dutGen: => T): TestBuilder[T] =
new TestBuilder(dutGen, autoResetEnabled, resetCycles)

/**
* Builder class to support .withAnnotations() chaining
*
* @param dutGen Generator for the device under test
* @param autoReset Whether automatic reset is enabled
* @param resetCyc Number of reset cycles to apply
*/
class TestBuilder[T <: Module](dutGen: => T, autoReset: Boolean, resetCyc: Int) {

/**
* Attach ChiselTest-style annotations before running the test.
*
* @param annotations Annotation list (for example WriteVcdAnnotation)
* @return A runner that executes the test with the provided annotations
*/
def withAnnotations(annotations: Seq[Any]): TestRunner[T] = {
new TestRunner(dutGen, autoReset, resetCyc, annotations)
}

/**
* Run the test body without explicit annotations.
*
* @param body User-defined test body operating on the DUT instance
*/
// Allow direct execution without annotations
def apply(body: T => Unit): Unit = {
val chiselSim = new ChiselSim {}
chiselSim.simulate(dutGen) { dut =>
if (autoReset) {
applyReset(dut, resetCyc)
}
body(dut)
}
}
}

/**
* Runner class that executes the test with VCD support
*
* @param dutGen Generator for the device under test
* @param autoReset Whether automatic reset is enabled
* @param resetCyc Number of reset cycles to apply
* @param annotations Annotation list used to configure execution behavior
*/
class TestRunner[T <: Module](dutGen: => T, autoReset: Boolean, resetCyc: Int, annotations: Seq[Any] = Seq()) {

/**
* Execute the configured test run.
*
* @param body User-defined test body operating on the DUT instance
*/
def apply(body: T => Unit): Unit = {
// Check if WriteVcdAnnotation is present
val hasVcd = annotations.exists {
case _: chiseltest.WriteVcdAnnotation.type => true
case _ => false
}

val chiselSim = new ChiselSim {}

if (hasVcd) {
println("[ChiselTest Compat] WriteVcdAnnotation detected, enabling VCD trace generation...")

// Create backend modification to enable VCD tracing
implicit val backendMod: BackendSettingsModifications =
(settings: Backend.Settings) =>
settings match {
case vs: verilator.Backend.CompilationSettings =>
val vcdStyle = TraceStyle(
kind = TraceKind.Vcd,
traceUnderscore = false,
traceStructs = true,
traceParams = true,
maxWidth = None,
maxArraySize = None,
traceDepth = None
)
vs.withTraceStyle(Some(vcdStyle))
case other => other
}

// Simulate with VCD enabled
chiselSim.simulate(dutGen) { dut =>
chiselSim.enableWaves()

if (autoReset) {
applyReset(dut, resetCyc)
}
body(dut)
}
} else {
// Simulate without VCD
chiselSim.simulate(dutGen) { dut =>
if (autoReset) {
applyReset(dut, resetCyc)
}
body(dut)
}
}
}
}

/**
* Apply reset sequence to the DUT
*
* @param dut Device under test
* @param cycles Number of cycles reset stays asserted
*/
private def applyReset[T <: Module](dut: T, cycles: Int): Unit = {
toTestableReset(dut.reset).poke(true.B)
toTestableClock(dut.clock).step(cycles)
toTestableReset(dut.reset).poke(false.B)
}
}
14 changes: 14 additions & 0 deletions src/main/scala/chiseltest/DecoupledDriver.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0

package chiseltest

/**
* Compatibility placeholder for ChiselTest's DecoupledDriver class.
*
* Decoupled enqueue/dequeue helpers are provided via implicit extensions in
* `package object chiseltest`. Use `import chiseltest._` to access:
* - `enqueueNow`, `enqueueSeq`
* - `expectDequeueNow`, `expectDequeueSeq`
* - `initSource`, `initSink`
*/
final class DecoupledDriver
Loading