Recording every signal in every scope makes the simulator serialize every value change to disk. On an active design that commonly adds a large fraction of wall-clock time and produces wave files in the tens of gigabytes. Full dumps suit interactive debug of one failure, never a thousand-test regression.
// snippet
// Full dump: maximum visibility, maximum cost
initial begin
$dumpfile("waves.vcd");
$dumpvars(0, tb_top); // depth 0 means every level below tb_top
end
// Scoped dump: only the block under debug
initial begin
$dumpfile("waves.vcd");
$dumpvars(2, tb_top.dut.u_dma); // 2 levels under the DMA only
end
// Windowed dump: only the time region of interest
initial begin
$dumpfile("waves.vcd");
$dumpvars(1, tb_top.dut.u_dma);
$dumpoff; // start with recording paused
#1ms $dumpon; // open the window
#100us $dumpoff; // close it
endScope: dump the suspect block, not the chip. Cost scales with signals recorded.
Depth: prefer depth 2 over depth 0 to skip leaf-cell noise.
Window: open recording around the failure time learned from a previous run.
Format: vendor binary formats are far smaller and faster than VCD.
One display call is free. A million of them is formatting cost plus file I/O plus a log nobody can open. Verbosity must filter before formatting and switch at runtime through plusargs, with no recompile to go quiet.
A logging macro needs a begin and end wrapper. Without it the bare if inside the macro steals the else of any outer if. The safe form below keeps the guard before the format.
// snippet
int unsigned verbosity = 100; // default: regression-quiet
// `define LOG_HIGH(msg) \
// begin \
// if (verbosity >= 300) $display msg; \
// end
// Usage (guard runs before the format):
// `LOG_HIGH(("txn id=%0d at %0t", id, $time))LOG COST AT SCALE: 2,000-test nightly regression
per-test detail lines, 50K txns per test:
2,000 tests x 50,000 lines x about 120 bytes = about 12 GB of logs
plus formatting CPU plus filesystem pressure on the farm
regression-quiet profile (reports plus summary only):
2,000 tests x about 200 lines = about 50 MB
Same checking. Same coverage. All signal, no noise.Everything in this topic converges into one operational idea: exactly two run profiles, a fast regression profile and a full debug profile, with every visibility feature switchable at runtime. One compiled testbench, two behaviors, no per-run tinkering.
Feature | Regression (fast) | Debug (visible) |
|---|---|---|
Waves | Off | On, scoped and windowed |
Log verbosity | Reports plus summary | Full per-transaction detail |
Functional coverage | On, it is why the run exists | Off, not needed to debug |
Assertions | On, non-negotiable | On |
Debug pools and logs | Off | On |
Seed | Random, logged | Forced to the failing seed |
Switch | Use for | Why |
|---|---|---|
Plusargs at runtime | Waves, verbosity, debug pools | One binary serves both profiles |
Compile defines | Heavyweight instrumentation | Removes code whose cost survives a runtime guard |
Prefer plusargs by default and reserve defines for cost that survives a runtime guard, such as bound-in debug monitors or large static debug arrays. Coverage-off runs must never feed closure metrics.
POST-FAIL RERUN-WITH-WAVES FLOW
NIGHTLY REGRESSION (fast profile): 2,000 tests, no waves, quiet logs
1,997 pass and 3 fail
Harvest from each failure: test name, seed, plusargs, fail time T
RERUN (debug profile, 3 runs only): same seed, waves on, raised verbosity,
wave window opened around time T
Debug with full visibility, fix, confirm in the fast profile,
and return the test to the nightly pool.
Cost: visibility tax on 3 runs instead of 2,000.
Requirement: seeds logged, runs reproducible.regression: ./simv +ntb_random_seed=$SEED (script picks and logs SEED)
debug: ./simv +ntb_random_seed=7731 +WAVES +VERBOSITY=300
VCS spelling shown; each simulator has its own seed switch (Questa: -sv_seed).The workflow depends on one non-negotiable property: reproducibility. The same seed plus the same command line must produce the same behavior. Anything that breaks seed stability breaks the entire strategy.
Run the regression blind and rerun only the 3 failures with waves. The visibility tax then applies to 3 runs instead of 2,000, with the same debug information.
No, as long as closure metrics come from the regression-profile runs. Coverage-off reruns are for debug only and must never feed sign-off numbers.
One testbench in the two-profile style: the dump and the verbosity are plusarg-switched, detail lines are guarded, and the run self-checks the count.
timeunit 1ns; timeprecision 1ps;
module tb_two_profile;
logic clk = 0;
always #5 clk = ~clk;
logic [7:0] count = 0;
always_ff @(posedge clk) count <= count + 8'd1;
int unsigned verbosity = 100; // regression-quiet default
int unsigned seed = 1;
int high_msgs = 0;
int errors = 0;
initial begin
void'($value$plusargs("VERBOSITY=%d", verbosity));
void'($value$plusargs("SEED=%d", seed));
void'($urandom(seed)); // the logged seed seeds this thread's RNG
$display("SEED=%0d VERBOSITY=%0d", seed, verbosity);
if ($test$plusargs("WAVES")) begin
$dumpfile("waves.vcd");
$dumpvars(2, tb_two_profile); // scoped even in debug
end
end
initial begin
repeat (50) @(posedge clk);
#1;
for (int k = 0; k < 200; k++) begin
if (verbosity >= 300) begin
$display("txn k=%0d count=%0h", k, count);
high_msgs++;
end
end
if (count !== 8'd50) errors++;
// Either profile passes: detail lines appear only at verbosity 300 and up.
if (high_msgs !== (verbosity >= 300 ? 200 : 0)) errors++;
if (errors == 0) $display("PASS: 50 clocks counted and detail lines matched the verbosity profile");
else $fatal(1, "FAIL: count=%0d high_msgs=%0d", count, high_msgs);
$finish;
end
endmodule