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.

systemverilog
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
endmodule

Replaying 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.

bash
# 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 log

Seed 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.

diagram
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 recipe

Random 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.