Part 6 · Testbench Architecture · Intermediate
Seeds & Reproducibility
Per-run seeds from the command line, logging the seed, replaying failures, seed sweeps, and random stability.
One seed, one universe
Constrained-random stimulus is only useful if it is reproducible . The entire random behavior of a simulation derives from one root seed: same code + same seed = same transactions, same timing, same failure. A random failure you cannot replay is a bug report you cannot act on.
Simulators take the seed on the command line — conceptually +ntb_random_seed=N (VCS) or -svseed N (Xcelium) or -sv_seed N (Questa). The regression script picks a different seed per run; the TB's job is to print that seed in the log header so a failing log is self-describing.
module top;
int unsigned run_seed;
initial begin
// Tool consumes +ntb_random_seed itself; we also read it so the
// log header records exactly which universe this run lives in.
if (!$value$plusargs("ntb_random_seed=%d", run_seed))
run_seed = 1; // deterministic default for smoke runs
$display("=================================================");
$display(" TEST : %s", test_name);
$display(" SEED : %0d", run_seed);
$display(" DATE : %s", date_str);
$display("=================================================");
end
endmoduleReplaying a failure
When the nightly regression reports a failure, the triage loop is mechanical: read the seed from the log header, rerun the same test with the same seed and debug knobs turned up, and the failure reproduces at the same timestamp.
# Nightly run that failed (seed was randomized by the dispatcher)
simv +TESTNAME=burst_test +ntb_random_seed=482931 > run.log
# log: SEED : 482931 ... *** TEST FAILED *** (3 errors)
# Replay: same seed, more visibility
simv +TESTNAME=burst_test +ntb_random_seed=482931 \
+VERBOSITY=DEBUG +DUMP_WAVES=1 > replay.log
# Same transactions, same failure, now with waves + debug logSeed sweeps for coverage
The flip side of reproducibility: one seed explores one path through the constraint space. To close coverage you run the same test across many seeds — a seed sweep — and merge coverage afterward. Ten tests x twenty seeds beats two hundred hand-written directed tests for breadth.
SEED SWEEP
burst_test ──┬── seed 1 ──► hits short bursts, low addrs
├── seed 7 ──► hits long bursts
├── seed 42 ──► hits wrap-around boundary ← new bin!
├── ...
└── seed 991 ──► hits back-to-back errors ← new bin!
merge coverage across all runs → closure
any failing (test, seed) pair → exact replay recipeRandom stability — editing code changes the universe
SystemVerilog random stability is hierarchical : each thread and each object draws from its own RNG, seeded from its parent at creation time. This makes runs stable against unrelated changes — but only up to a point. Adding a new randomize() call, reordering object construction, or spawning a new process upstream re-seeds everything downstream, and your replayed seed no longer reproduces the old failure.
Replay failures on the exact code revision that failed — check out the commit, then rerun the seed.
Debug by adding $display, not by adding randomize() calls or new objects before the failure point.
Construct TB objects in a fixed order; conditional construction order changes shift seeding.
If you must edit code, re-confirm the failure still reproduces before trusting the debug session.
Interview angle
"A regression failure does not reproduce — why?" — different seed, different code revision, or random-stability shift from an edit.
"Why print the seed instead of relying on the dispatcher's records?" — the log must be self-contained; logs outlive dispatch databases.
"How do seeds relate to coverage closure?" — each seed is one sample of the constraint space; sweeps + merge give closure.
Key takeaways
Same code + same seed = same run; that equation is the entire debug strategy for random TBs.
Print the seed in the log header — every failing log must contain its own replay recipe.
Seed sweeps trade one directed test for breadth across the constraint space.
Random stability is hierarchical and fragile to edits — replay on the failing revision.
Common pitfalls
Running with the tool's default seed everywhere — every run explores the identical path.
Seed recorded only in the dispatcher, not the log — the log alone cannot be replayed.
Adding code during debug and silently changing the random sequence — failure vanishes.
Assuming time-based seeds are logged somewhere — if nothing prints it, the run is lost.