Part 7 · Advanced & Integration · Intermediate

Wave Dumping & Runtime Control

$dumpfile/$dumpvars scope control, FSDB conceptually, plusarg-driven dump windows, $finish vs $stop vs $fatal, and runtime verbosity.

Controlling what gets dumped

$dumpfile names the VCD output and $dumpvars(depth, scope) selects how much hierarchy to record: depth 0 means the scope and everything below it, depth 1 means only the scope's own signals. Dump scope is the single biggest lever on both simulation speed and disk usage — full-design dumps routinely double run time and produce files in the tens of gigabytes. Commercial flows use FSDB (or vendor equivalents) instead of VCD: same scoping concepts, dramatically better compression, driven by tool-specific system tasks.

systemverilog
initial begin
  $dumpfile("waves.vcd");

  // depth 0 = this scope and EVERYTHING below — expensive
  $dumpvars(0, tb_top);

  // Better: target the suspect block only
  // $dumpvars(0, tb_top.dut.u_dma);     // one subtree
  // $dumpvars(1, tb_top.dut);           // dut's own signals, no children

  // Multiple targeted calls compose:
  // $dumpvars(0, tb_top.dut.u_dma);
  // $dumpvars(1, tb_top.dut.u_arb);
end

// FSDB conceptually identical (Verdi flow):
//   $fsdbDumpfile("waves.fsdb");
//   $fsdbDumpvars(0, tb_top.dut.u_dma);

Dump windows: record only around the failure

A 10-millisecond regression run that fails at 9.2 ms does not need 9 ms of waves. $dumpoff and $dumpon gate recording at run time; drive the window edges from plusargs and the rerun of a failing seed records only the region around the failure — small file, fast run, full visibility where it matters.

systemverilog
// Plusarg-driven dump window
time dump_start = 0;
time dump_stop  = 0;          // 0 = run to the end

initial begin
  if (!$test$plusargs("DUMP")) begin
    // no +DUMP → no wave overhead at all
  end else begin
    void'($value$plusargs("DUMP_START=%d", dump_start));
    void'($value$plusargs("DUMP_STOP=%d",  dump_stop));

    $dumpfile("waves.vcd");
    $dumpvars(0, tb_top.dut);

    if (dump_start > 0) begin
      $dumpoff;                       // armed but not recording
      #(dump_start) $dumpon;          // open the window
    end
    if (dump_stop > dump_start)
      #(dump_stop) $dumpoff;          // close it
  end
end

// First run (no waves):   simv +TESTNAME=stress
//   → fails at 9_200_000 ns
// Rerun with a window:    simv +TESTNAME=stress +DUMP \
//                              +DUMP_START=9000000 +DUMP_STOP=9400000
diagram
DUMP WINDOW STRATEGY

  full dump          ██████████████████████████████  20 GB, 2x slower
                     0ms                        10ms

  windowed dump      ░░░░░░░░░░░░░░░░░░░░░░░░██░░░  0.4 GB, ~1.05x
                                          ▲  ▲
                                  DUMP_START  failure @9.2ms
                                       9.0ms  DUMP_STOP 9.4ms

  regression default: +DUMP absent  zero wave cost
  failure rerun:      +DUMP +window around the failing time

Ending the run: $finish, $stop, $fatal — and verbosity

Three ways out

  • $finish — normal termination: ends the process, runs final blocks; the standard end of a passing or cleanly failing test.

  • $stop — pause, not exit: drops into the interactive debugger prompt; useful at a breakpoint moment, but it hangs batch regressions waiting for input.

  • $fatal(status, msg) — error termination: prints a fatal message and finishes with a status; the status argument feeds the exit code on most tools, letting scripts distinguish failure kinds.

  • Exit-code discipline: the regression script must see nonzero on failure — check your tool's mapping of $fatal status to process exit code, and grep logs as a backstop.

systemverilog
// Runtime verbosity: one knob, checked by a log helper
int verbosity = 1;   // 0=errors only, 1=normal, 2=debug

initial void'($value$plusargs("VERBOSITY=%d", verbosity));

function void log_msg(int level, string msg);
  if (level <= verbosity)
    $display("[%0t] %s", $time, msg);
endfunction

// Timeout guard — every testbench needs one:
initial begin
  #50ms;
  $fatal(1, "TIMEOUT: test did not complete");
end

// End-of-test:
task end_of_test(int errors);
  if (errors == 0) begin
    $display("TEST PASSED");
    $finish;                       // exit code 0
  end else
    $fatal(1, "TEST FAILED: %0d errors", errors);  // nonzero exit
endtask

Key takeaways

  • Dump scope and depth are the biggest sim-speed levers — regressions run dump-free, reruns dump a window.

  • Drive $dumpon/$dumpoff windows from plusargs so the failing seed reruns with waves only around the failure.

  • $finish ends, $stop pauses for interaction (never in batch), $fatal ends with an error status for scripts.

  • Make verbosity a runtime plusarg — re-running at higher verbosity must not require a recompile.

Common pitfalls

  • $dumpvars(0, tb_top) as the regression default — gigabytes of waves and doubled run time for passing tests.

  • $stop in code that runs under batch regression — the job hangs at an interactive prompt until killed.

  • Passing test paths that never call $finish — the timeout guard fires and a green test reports red.

  • Trusting exit codes without verifying the tool's $fatal status mapping — scripts mark failing tests as passed.

Interview angle

Expect: $finish vs $stop vs $fatal (exit, pause, error-exit with status); how to cut wave overhead in regression (no default dump, plusarg-windowed rerun); and the flow question — describe your debug loop when a regression seed fails. The expected answer: rerun the seed with +DUMP and a window around the failure time, at higher verbosity, without recompiling.