Getting Started

A first testbench in three small files, running on a free simulator — then the complete TinyALU testbench.

Prerequisites

You need two tools, both free:

No Python, no interpreter, no virtual environment. A rustdv testbench is a native library the simulator loads directly.

Hello, simulator

A rustdv testbench is an ordinary Rust library crate, and three small files make a running one. Start with the layout:

mkdir -p hello_tb/src
cd hello_tb

First, Cargo.toml — the one line that matters is crate-type, which tells cargo to build a shared library the simulator can load:

# Cargo.toml
[package]
name = "hello_tb"
version = "0.1.0"
edition = "2021"

[lib]
path = "src/hello_tb.rs"     # the crate root, named after the crate
crate-type = ["cdylib"]      # a shared library, loaded by the simulator

[dependencies]
rustdv = "0.1"               # or run: cargo add rustdv

Second, the testbench itself, in src/hello_tb.rs:

// src/hello_tb.rs
use rustdv::prelude::*;

rustdv::vpi_bootstrap!();    // exports the entry points the simulator loads

#[rustdv::test]
async fn hello(ctx: RustdvCtx) -> Result<(), TestError> {
    let dut = ctx.dut();
    let clk = dut.signal("clk")?;
    for _ in 0..3 {
        clk.rising_edge().await;
        ctx.info(&format!("saw a rising edge at {} ns", sim_time_ns()));
    }
    Ok(())
}

Read it the way the executor does. #[rustdv::test] registers the function with the test runner. The ctx argument is the framework, handed to you: the DUT handle, logging, the random source. dut.signal("clk")? looks the signal up by name — a typo is an Err, not a crash. And clk.rising_edge().await is simulated time: the test suspends, the simulator advances, and the executor resumes the test at the edge.

Third, something to simulate. The DUT here is just a clock:

// hello.sv
`timescale 1ns/1ns
module hello;
   reg clk = 0;
   always #5 clk = ~clk;
endmodule

Run it

Build the testbench, give the library the .vpi name the simulator expects, compile the DUT, and run:

cargo build --release
cp target/release/libhello_tb.so hello_tb.vpi     # macOS: libhello_tb.dylib
iverilog -g2012 -o hello.vvp -s hello hello.sv
vvp -M . -m hello_tb hello.vvp

The output — this transcript is a real run:

      0.00ns INFO     rustdv: found 1 test(s), RUSTDV_RANDOM_SEED=1786027686
      0.00ns INFO     running hello (1/1)  [src/hello_tb.rs:5]
      5.00ns INFO     [hello]: saw a rising edge at 5 ns
     15.00ns INFO     [hello]: saw a rising edge at 15 ns
     25.00ns INFO     [hello]: saw a rising edge at 25 ns
     25.00ns INFO     hello PASSED
******************************************************************************
** TEST                                       STATUS  SIM TIME (ns)      **
******************************************************************************
** hello                                        PASS          25.00      **
******************************************************************************
REGRESSION: PASS

That is the whole loop: no testbench module in the Verilog — rustdv drives the top module itself — and no environment to activate. The runner found the test, seeded the random source (set RUSTDV_RANDOM_SEED to make a run reproducible), ran the test to completion, and printed the regression table. If the test returned an Err, the table would say FAIL and vvp would exit nonzero — ready for CI.

The real thing: the TinyALU testbench

The five-line test above is the mechanics. The point of rustdv is the UVM-style testbench built on top of them: environments, agents, a driver and monitors connected by TLM, a scoreboard subscribing through analysis ports, sequences feeding the driver. The repository ships one, complete, for the TinyALU:

git clone https://github.com/rustdv/rustdv.git
cd rustdv
sim/run_rustdv.sh

It builds the testbench, compiles the DUT, runs randomized and directed tests against it, and ends the same way every rustdv run ends: REGRESSION: PASS — with every operation covered and every result checked against a Rust scoreboard. Zero install works too: open the repository in GitHub Codespaces and you get Rust, Icarus Verilog, and Verilator ready to run everything.

Where to go next