# 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 `SvsimCompilationSettings`, 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).
