Part 6 · Testbench Architecture · Intermediate

Test Knobs via Plusargs

$test$plusargs and $value$plusargs, verbosity/txn-count/error-rate knobs, documentation, and config layering.

Recompile nothing, control everything

A regression cannot recompile per run — the same compiled snapshot must serve smoke tests, stress tests, and debug replays. Plusargs are the runtime control surface: $test$plusargs("NAME") tests presence of a flag, $value$plusargs("NAME=%d", var) parses a value. The TB reads them once at time zero into a config object, and the rest of the environment reads the config — components never call plusarg functions directly.

systemverilog
class tb_config;
  int unsigned num_txns   = 200;   // sane defaults: bare sim runs fine
  int unsigned error_rate = 0;     // percent of injected protocol errors
  string       verbosity  = "INFO";
  bit          dump_waves = 0;

  function void parse_plusargs();
    void'($value$plusargs("NUM_TXNS=%d",   num_txns));
    void'($value$plusargs("ERROR_RATE=%d", error_rate));
    void'($value$plusargs("VERBOSITY=%s",  verbosity));
    if ($test$plusargs("DUMP_WAVES")) dump_waves = 1;

    $display("[CFG] num_txns=%0d error_rate=%0d%% verbosity=%s waves=%0d",
             num_txns, error_rate, verbosity, dump_waves);
  endfunction
endclass
bash
# Same binary, three very different runs:
simv +NUM_TXNS=50                          # quick smoke
simv +NUM_TXNS=5000 +ERROR_RATE=10         # stress with error injection
simv +NUM_TXNS=200 +VERBOSITY=DEBUG +DUMP_WAVES   # failure replay

The standard knob set

Knobs every class-based TB grows

  • VERBOSITY — log level (ERROR/WARN/INFO/DEBUG); regression runs at INFO, replays at DEBUG.

  • NUM_TXNS — transaction count; scales the same test from 30-second smoke to hour-long soak.

  • ERROR_RATE — percent of stimulus carrying injected protocol errors; 0 for clean runs.

  • TESTNAME — selects which test/generator variant to run from one compiled image.

  • DUMP_WAVES — waveform capture off by default; regressions at scale cannot afford waves.

Knobs feed constraints naturally — the generator randomizes around the knob value rather than using it verbatim:

systemverilog
class generator;
  tb_config cfg;
  task run();
    repeat (cfg.num_txns) begin
      txn t = new();
      // knob shapes the distribution; randomization fills the rest
      if (!t.randomize() with {
            inject_err dist { 1 := cfg.error_rate,
                              0 := 100 - cfg.error_rate };
          })
        tb_status::report_error("GEN", "randomize failed");
      out_mb.put(t);
    end
  endtask
endclass

Documentation and config layering

An undocumented knob is a trap: someone passes +ERR_RATE=10 instead of +ERROR_RATE=10, $value$plusargs silently misses it, and the run quietly tests nothing. Keep a knob table in the TB README and print every effective value at time zero — the [CFG] line above doubles as the runtime documentation.

diagram
CONFIG LAYERING (lowest to highest priority)

  layer 1: class defaults        tb_config fields (always-sane values)
              │  overridden by
  layer 2: test                  test code mutates cfg before build
              │  overridden by
  layer 3: command line          plusargs parsed LAST
              ▼
        final cfg object ──► passed down: env  agents  gen/drv/mon

  rule: components read cfg fields only.
        $value$plusargs appears in exactly one place — tb_config.

Interview angle

  • "How do you control a test without recompiling?" — plusargs parsed once into a config object, layered over defaults.

  • "Why centralize plusarg parsing?" — one source of truth, printable at time zero, no scattered hidden flags.

  • "Plusargs vs config object?" — plusargs are the outside interface; the config object is the inside distribution mechanism.

Key takeaways

  • Plusargs let one compiled image serve smoke, stress, and replay runs.

  • Parse plusargs in one place into a config object; components read only the config.

  • Defaults must be sane — a bare sim with no plusargs should pass.

  • Print every effective knob at time zero; an undocumented knob is a silent no-op.

Common pitfalls

  • Misspelled plusarg silently ignored — run executes with defaults and nobody notices.

  • $value$plusargs calls scattered through components — impossible to audit what a run did.

  • No default values — TB crashes or hangs when a knob is omitted.

  • Verbosity knob that only gates new code — legacy $display floods DEBUG-level replays.