Skip to main content

# Migrating from ChiselTest to ChiselSim

## Background​

With the release of Chisel 5, Chisel moved off of the legacy Scala FIRRTL Compiler (SFC) to the MLIR FIRRTL Compiler (MFC), part of the llvm/circt project. After this release, the Scala FIRRTL Compiler was no longer maintained. This change in underlying compiler technology has been a crucial piece in enabling the addition of many new features to Chisel, including linear-temporal logic (LTL) properties, Probes, and Layers. Unfortunately, Chisel 3's testing library, ChiselTest, is built around the SFC, making it difficult to support ChiselTest in Chisel 5 and beyond. ChiselTest is not used or maintained by the core Chisel development team or their employers.

ChiselSim is the approved replacement for ChiselTest in Chisel 5 and beyond. ChiselSim is maintained and used by the core Chisel development team. This page describes how to migrate from ChiselTest to ChiselSim.

## Getting Started​

The developers of ChiselTest have maintained some amount of compatibility between ChiselTest and newer versions of Chisel. This relies on a forked version of the SFC. Use of ChiselTest with Chisel 6 or later will prevent the usage of new Chisel 6 features. It is not expected that new versions of Chisel will be compatible with the SFC.

We recommend using the latest minor version of Chisel 5 and ChiselTest 5 for migrating. At the time of writing this is Chisel v5.1.0 and ChiselTest v5.0.2, but please check for later versions.

## Migration​

ChiselSim provides a minimal `peek`, `poke`, `expect`, and `step` API, similar to that of ChiselTest. You use ChiselSim by importing it `import chisel3.simulator.EphemeralSimulator._`, and using its `simulate` method which is similar to ChiselTest's `test`. At present, it does not have any integration with ScalaTest, so users should use any ScalaTest APIs directly.

For example, given a simple design (typically in `src/main/scala`):

    import chisel3._
    class MyModule extends Module {
      val io = IO(new Bundle {
        val in = Input(UInt(16.W))
        val out = Output(UInt(16.W))
      })

      io.out := RegNext(io.in)
    }

The legacy ChiselTest way to test this would be with a `ChiselScalatestTester` in `src/test/scala`:

    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(0.U)
          c.clock.step()
          c.io.out.expect(0.U)
          c.io.in.poke(42.U)
          c.clock.step()
          c.io.out.expect(42.U)
          println("Last output value : " + c.io.out.peek().litValue)
        }
      }
    }

This can be rewritten using ChiselSim as follows:

    import chisel3._
    import chisel3.simulator.EphemeralSimulator._
    import org.scalatest.flatspec.AnyFlatSpec

    class MyModuleSpec extends AnyFlatSpec {
      behavior of "MyModule"
      it should "do something" in {
        simulate(new MyModule) { c =>
          c.io.in.poke(0.U)
          c.clock.step()
          c.io.out.expect(0.U)
          c.io.in.poke(42.U)
          c.clock.step()
          c.io.out.expect(42.U)
          println("Last output value : " + c.io.out.peek().litValue)
        }
      }
    }

For both ChiselTest and ChiselSim, you will typically run this with `sbt test` or some way of running tests. The output from ChiselSim will look something like the following:

    // This is how one can run a ScalaTest Spec manually, typically one would use "sbt test"
    org.scalatest.nocolor.run(new MyModuleSpec)

    Last output value : 42
    MdocSession$MdocApp$MyModuleSpec:
    MyModule
    - should do something

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.