Start at the first-error time and walk backward through the cone of logic. At each stage, ask which input was already wrong.
BACKWARD TRACE — symptom to origin
symptom @ 12,450 ns: dut.rdata = 0x00 (expected 0xA4)
│
▼ what drives rdata?
mem_rd_data was 0x00 ◄── already wrong? YES → keep going back
│
▼ what drives mem_rd_data?
mem array @ addr 0x1F4 holds 0x00 ◄── wrong? YES → when was it written?
│
▼ search last write to 0x1F4
@ 9,120 ns: wr_en=1, wr_addr=0x1F4, wr_data=0xA4 ◄── write was CORRECT
│
▼ so the write was lost — look between 9,120 and 12,450
@ 9,121 ns: mem_clr pulse ◄── ORIGIN: clear fired mid-traffic
│
▼ what asserted mem_clr? ... and so on, into the bug's home stageTwo viewer tools speed this up: driver and load tracing, and jumping to previous edges instead of scrolling. Repeat until a stage has correct inputs but a wrong output.
The first-divergence cycle is the earliest point where DUT state differs from expectation. Everything before it is provably fine, which kills whole families of wrong theories.
Diff a passing seed against the failing one, aligned on a common event, and bisect to the first differing cycle.
Diff the reference model per transaction, then open waves only around the first disagreeing transaction.
Drop an immediate assertion on the suspect invariant and re-run the minimized case; its first failure time is the marker.
Group signals by interface and name markers such as first_err and divergence. A saved session resumes tomorrow where it stopped.
Concurrent assertions sample in the Preponed region: the settled value just before the clock edge. The viewer draws the post-edge value at that timestamp instead.
An assertion can therefore fire on a cycle where the wave looks correct. Judge assertion failures against the previous-slot sampled values, or use the simulator assertion view.
Watch d and q. The display and the sampler disagree exactly when d moves at the edge.
Slot 0: d 0, q 0. Settled before the experiment.
Slot 1: d reads 1 (changed at this very edge) but q is still 0. The wave shows 1 where the flop saw 0.
Slot 2: d reads 0 (changed at this edge) while q is 1. Display and sample disagree again.
Slot 3: both settled at 0. The naive rule holds only when nothing moves at the edge.
module pre_dut (
input logic clk,
input logic d,
output logic q
);
logic q_r = 0;
always_ff @(posedge clk) q_r <= d;
assign q = q_r;
endmodule
The bench models the sampler explicitly: it records the settled value before the edge, moves d just after the edge so the outcome is identical on every engine, and checks the flop against the recorded value. A change exactly at the edge would leave the order to the scheduler.
Two more artifacts deserve the same suspicion. Same-timestamp events have a delta order the viewer may flatten, so expand delta time around clock gating. An undumped signal shows no data at all, so confirm the dump scope before trusting a flat line.
The sampled value from the slot before the edge. The display shows the post-edge value, which the assertion never saw.
A bus environment reported sporadic mismatches after an RTL drop. The narrative said RTL, so the playbook said check the pins first.
TIMELINE — Case 1 (monitor sampling-edge fault)
T+0:00 Regression: 7/400 seeds fail, all "SB data mismatch".
T+0:10 Triage: first error only; single-field mismatch, and act
of txn N equals exp of txn N-1 in 3 of 3 cases. SHIFTED.
T+0:25 Minimize: 6-txn directed repro fails identically. Gold.
T+0:40 Verdict test at the pins: wave shows DUT rdata CORRECT
at the protocol sample point. DUT acquitted → TB fault.
T+1:05 Backward trace in the MONITOR: it samples rdata a fixed
N cycles after valid; new RTL legally responds with 0 wait
states (spec allows 0-wait responses). The fixed count lands
on the wrong beat → captures the PREVIOUS beat's data.
T+1:20 Fix: monitor samples per spec (valid && ready), not on a
fixed wait count. Add a 0-wait-state directed test.The tell was visible in ten minutes: actual data matched the previous expected data. Time-shifted data means a sampling-position fault.
Generalized lesson: monitors must encode the protocol timing range, not the timing the current RTL exhibits. The fix added a zero-wait directed test.