118 lines
6.5 KiB
Markdown
118 lines
6.5 KiB
Markdown
# svsim
|
|
|
|
`svsim` is a low-level library for compiling and controlling SystemVerilog
|
|
simulations, currently targeting Verilator and VCS as backends.
|
|
|
|
## Design
|
|
|
|
In no particular order, here are some of the design considerations of `svsim`:
|
|
* `svsim` does not know about Chisel. The inputs to `svsim` are SystemVerilog
|
|
files and the `ModuleInfo` class, which encodes all the information `svsim`
|
|
needs to generate a test harness for the supplied `SystemVerilog`.
|
|
* `svsim` **is not a testing framework**, and is not opinionated about how a
|
|
higher-level testing framework should be built. `svsim` should enable
|
|
SystemVerilog simulation for any testing framework and should not be tied to
|
|
any particular technology (i.e. Scalatest).
|
|
* `svsim` provides a backend-independent test harness. This means that `svsim`'s
|
|
generated sources are not impacted by the choice of backend and indeed can be
|
|
shared between multiple backends. If backend-specific behavior is required, it
|
|
is provided via a compile-time flag (such as
|
|
`SVSIM_BACKEND_SUPPORTS_DELAY_IN_PUBLIC_FUNCTIONS`).
|
|
* `svsim` simulations are also backend-independent, meaning that the same code
|
|
launches and controls a simulation regardless of what backend it was compiled
|
|
using.
|
|
* `svsim` backends are declarative. This means a backend only controls which
|
|
executable is invoked for compilation, and what arguments are provided to that
|
|
executable (either via command line or environment variables). Backends may
|
|
also provide arguments to the simulation, but may not have any other
|
|
specialized logic.
|
|
* Simulations are controlled using an ad-hoc protocol where commands are read
|
|
from `stdin` and messages are written to `stdout`. This protocol is **not**
|
|
meant to be an ABI, and can change freely between Chisel verisons.
|
|
* Communication with the simulation is pipelined, so the Scala driver can send
|
|
multiple commands before processing responses from the simulation. This allows
|
|
`svsim` to effectively eliminate the overhead of communicating with the
|
|
simulation without resorting to JNA or Scala-side caching. This optimization
|
|
can, of course, be defeated if the driver code waits on every command, but we
|
|
consider this an antipattern.
|
|
* `svsim` strives to have the minimum amount of dependencies possible, so that
|
|
it may be maximally portable.
|
|
* `svsim` does not provide syntactic sugar, and expects that higher level
|
|
frameworks will implement whatever sugar they thing is appropriate. For
|
|
example, if a framework like `chiseltest` were to use `svsim` under the hood,
|
|
it might choose to store the `Simulation.Controller` in a `DynamicValue` to
|
|
enable calling `peek` and `poke` directly on a Chisel `Data`, or pass
|
|
information via annotations. Such specialization should stay in the
|
|
higher-level framework and all `svsim` APIs should stay explicit.
|
|
|
|
## Architecture
|
|
|
|
We consider it a top priority to keep `svsim`'s architecture and implementation
|
|
as simple as possible. In support of this effort, there are really only three
|
|
components to `svsim`:
|
|
|
|
### Workspace
|
|
|
|
The `Workspace` manages all interaction with the filesystem. Its API is mutable,
|
|
because the underlying representation (files on disk) is mutable. A `Workspace`
|
|
is represented on the filesystem as a folder. The `reset()` method will delete
|
|
any previous state and create the necessary folders. The `elaborate()` method
|
|
takes a `ModuleInfo` which describes the SystemVerilog module to be simulated.
|
|
The idea is that a higher-level framework will provide specialized `elaborate`
|
|
methods which emit SystemVerilog to `primarySourcesPath`, similar to what we do
|
|
with `elaborateGCD()` in `src/test/scala/Resources.scala`. For example, Chisel
|
|
can provide an `elaborate[T <: RawModule](module: => T)` method.
|
|
`generateAdditionalSources()` uses the `ModuleInfo` from `elaborate` and
|
|
generates the test harness. Finally, `compile()` uses a `Backend` to compile the
|
|
simulation. Because `svsim`'s test harness is backend-independent, `compile` may
|
|
be called multiple times with different settings or different backends to create
|
|
multiple `Simulation`s (a `workingDirectoryTag` is provided so that mutliple
|
|
simulations are output to separate directories).
|
|
|
|
### Simulation
|
|
|
|
The `Workspace`'s `compile` method returns a `Simulation` instance. A
|
|
`Simulation` is launched using the `run` method, which passes a
|
|
`Simulation.Controller` instance to the provided closure. The
|
|
`Simulation.Controller` is used to explicitly control the `Simulation`, and
|
|
should not escape the body of the closure. Like most of `svsim`,
|
|
`Simulation.Controller` aims to be a low level API on which higher level APIs
|
|
can be built. For example, `Simulation.Controller`'s `get` method does not
|
|
implicitly run the simulation to make the effects of previous `set` calls
|
|
visible, the way you would expect for a peek/poke test. To evaluate the
|
|
simulation state, the user must explicitly call `controller.run(0)`. This, of
|
|
course, does not prevent a peek/poke style API from being built using these
|
|
building blocks in a higher-level framework.
|
|
|
|
### Backend
|
|
|
|
`svsim` currently provides two `Backend`s: `verilator.Backend` and
|
|
`vcs.Backend`. Settings which **all** `svsim` `Backend`s must support (like
|
|
SystemVerilog preprocessor defines) live in `CommonCompilationSettings`, and each
|
|
backend has its own backend-specific `CompilationSettings`.
|
|
|
|
## Advanced Usage
|
|
|
|
### `make simulation`
|
|
|
|
Because `Backend`s are declarative, `svsim` is able to output a `Makefile` for
|
|
compiling the simulation. This `Makefile` can be re-invoked after the fact to
|
|
rebuild the simulation without re-running the Scala driver (`make simulation`).
|
|
Users are free to modify either the harness generated by `svsim`
|
|
(`generated-sources`), the SystemVerilog provided to `svsim`
|
|
(`primary-sources`), or even the arguments passed to the compiler
|
|
(`workdir-$tag/Makefile`) and these changes will be picked up when running `make
|
|
simulation`.
|
|
|
|
### `make replay`
|
|
|
|
`svsim` by default emits an `execution-script.txt` when running a Simulation.
|
|
This script captures all commands sent to the simulation, which enables `svsim`
|
|
to add a second target to the `Makefile`: `make replay`. `make replay` rebuilds
|
|
the simulation, picking up any changes as mentioned above, and then replays the
|
|
commands that were sent by `Simulation.Controller` verbatim. This allows
|
|
replaying most tests without having to re-run the Scala code, potentially with
|
|
manual modifications to either the SystemVerilog or the test harness (though
|
|
tests which modify their behavior based on values read from the `Simulation` may
|
|
not be replayed faithfully).
|