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:
- The Rust toolchain. Install it with
rustup; you get
cargo, the build tool every command below uses. - Icarus Verilog (
iverilogandvvp). On Ubuntu/Debian:apt install iverilog. On macOS:brew install icarus-verilog. Or build it from source.
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
- The Interlude — the complete TinyALU testbench in one place, the best single answer to “what does rustdv code look like?”
- Rust for RTL Verification — the companion book. No prior Rust assumed; forty chapters from the language basics to virtual sequences.
- What rustdv provides —
the complete reference for everything
use rustdv::prelude::*brings into scope. - UVM translation tables — your SystemVerilog UVM or cocotb/pyuvm vocabulary, mapped onto rustdv name by name.
- GitHub Discussions — the front door for everything. Issues and pull requests are open too; what you found, what you built, and what rustdv should become all belong in the discussion.
rustdv