DV Methodology

Chapter 14 of 15

The Diagnostic Method: Isolate, Instrument, Hypothesize, Confirm

The repeatable four-step process that actually confirms a hypothesis ch2's signature-reading only suggested -- taught by comparing how it played out differently for a real structural bug and a real timing bug, both already documented on axil_regfile, without retelling either incident in full.

Ch2 read a failure's signature to form a fast first hypothesis about which of ch1's four categories it belongs to. A hypothesis isn't a diagnosis. This chapter is the method that turns one into the other — four steps, applied to two real incidents this site already has on record: the combinational loop (uvm-advanced ch1) and the BVALID/irq race (uvm-advanced ch4). Both are cited here, not retold — this chapter's job is the method itself, and the same four steps looked different applied to a structural bug versus a timing bug.

Four steps

  1. Isolate — reduce the failure to the smallest piece of the system that still shows it, removing everything not necessary to trigger it.
  2. Instrument — add whatever visibility doesn't already exist, so the failure (or the specific risk suspected) becomes directly observable instead of inferred.
  3. Hypothesize — state one specific, falsifiable claim about the root cause, precise enough that a specific change would prove or disprove it.
  4. Confirm with a minimal, targeted change — make the smallest change the hypothesis actually calls for, not a broader rewrite, then re-verify.

The same four steps, two different shapes

StepCombinational loop (structural, DUT)BVALID/irq race (timing, testbench)
IsolateThe testbench was already minimal; isolation meant narrowing to the specific signal dependency chain itself — s_axi_awready → write_fire → aw_fire → s_axi_awready — not building a smaller test.Isolation meant pulling two specific signals, bvalid and irq, out of the full multi-agent environment's scheduling, away from every other driver, monitor, and sequence running alongside them.
InstrumentThe simulator's own scheduler was the instrumentation — a genuine combinational loop has no stable solution, so simulating it (not just reading it) surfaced the problem immediately, with nothing custom added.Nothing surfaces relative event ordering by default; this needed deliberately-added visibility — two always blocks, one per signal, each printing $time on its own trigger.
HypothesizeOne precise, falsifiable claim: s_axi_awready depends combinationally on write_fire, which depends on aw_fire, which depends on s_axi_awready — a true zero-delay cycle with no stable value, not a timing quirk.One precise, falsifiable claim: the driver unblocking on BVALID and the monitor firing on irq are independent processes with no ordering guarantee on the same edge, so wait_trigger() called after write() returns can already be too late.
Confirm, minimallyRemove exactly the && !write_fire term from the two ready-signal assignments — nothing else touched — then re-simulate to confirm the loop is gone and every previously-passing scenario still passes.Wrap the existing wait_trigger() and write() calls in fork...join instead of restructuring the sequence — then re-run to confirm the result no longer depends on scheduling luck.

Both root causes are genuinely different — one purely structural, needing no runtime timing at all to exist; one purely about relative timing between independent processes, with nothing structurally wrong in either piece of code. The method didn't change between them. What each step actually meant did.

Why the smallest change, not the safest-feeling one

A broad rewrite that happens to make a failure go away is a worse outcome than it looks, not a better one — it can mask whether the hypothesis was ever actually right. If the combinational-loop fix had also touched three other unrelated always blocks "to be safe," a passing re-simulation wouldn't confirm the loop hypothesis specifically; it would leave open whether the loop was fixed at all, or just papered over by something else that changed at the same time. Both real fixes in this project's history were small on purpose — one term removed, one fork added — because a minimal targeted change is the only kind that actually confirms the hypothesis it was built to test, rather than confirming nothing in particular while happening to work.

Summary

  • The method is four steps: isolate the smallest reproducer, instrument to make the failure observable, state one falsifiable hypothesis, confirm with the smallest change the hypothesis calls for.
  • "Isolate" doesn't always mean building a smaller test — for the combinational loop, the testbench was already minimal, and isolation meant narrowing to the specific signal dependency chain instead.
  • "Instrument" can mean simply running the simulator and reading its own complaint (a structural bug like a combinational loop surfaces itself) or deliberately adding custom visibility (a race needs $time-printing probes, since nothing flags relative ordering by default).
  • A good hypothesis is precise enough to be falsifiable — not "something's wrong with the write logic," but a specific claim naming the exact dependency or the exact ordering assumption at fault.
  • A minimal, targeted fix is what actually confirms a hypothesis; a broader change that happens to make the symptom disappear leaves open whether the real root cause was ever found at all.

For the combinational-loop bug, 'isolate' didn't mean building a smaller testbench -- the existing one was already minimal. What did isolation mean in that case?

The combinational loop was found by simulating the RTL with no custom instrumentation added, while the BVALID/irq race required deliberately adding $time-printing always blocks. Why the difference?

What makes 'the driver unblocking on BVALID and the monitor firing on irq are independent processes with no ordering guarantee on the same edge' a good hypothesis, compared to 'something about timing seems off'?

Why does this chapter argue a minimal, targeted fix is better than a broader change that also happens to make a failure disappear?