Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Part 0 · What Debugging Is6
  • Part 1 · Reproduce8
  • Part 2 · Observe9
  • Part 3 · Localise10
  • Part 4 · Hypothesise and Test6
    • A Hypothesis Must Predict
    • The Experiment That Splits Suspects
    • Keeping Several Hypotheses Alive
    • Confirmation Bias and Blaming the Tool
    • One Change at a Time
    • The Debug Log
  • Part 5 · Bug Classes16
  • Part 6 · Debug by Stage9
  • Part 7 · Hangs and Deadlocks7
  • Part 8 · Intermittent Failures7
  • Part 9 · Fix, Prove, Prevent7
  • Part 10 · Case Studies12
  • Part 11 · Debug as a Team5
  • Part 12 · Interview Prep5
Loading lesson

‹Part 4 · Hypothesise and Test
  1. Topics
  2. The Art of Hardware Debug
  3. Part 4 · Hypothesise and Test
  4. The Experiment That Splits Suspects

Part 4 · Hypothesise and Test · Working (W)

The Experiment That Splits Suspects

With several suspects and time for one run, write down what each candidate experiment would show under each suspect and pick the one whose outcomes split the suspects most evenly by probability, at the lowest cost. A real dbglab failure from a random-start-value regression, settled in two runs.

On this page

    Why this matters

    With four suspects, most runs you could do are predicted identically by all of them or remove only one, and a day goes on experiments that each rule out a quarter. A table of suspects against predicted outcomes finds the run that halves them before you spend anything.

    After this lesson you can: list three to five suspects as mechanisms, tabulate what each candidate run would show under each, and choose the run that best splits them, weighing how likely each suspect is and what the run costs.

    Note

    Level: Working (W) · Debug-loop step: Test

    Before this: Guessing vs Searching, A Hypothesis Must Predict

    One run before the triage meeting

    The nightly regression now runs dbglab's random test in Verilator with random start values (XRAND=1), the closest a simulation gets to a chip powering up. Seed 29 fails:

    Terminal
    $ XRAND=1 tb/run.sh random 29 verilator
    dbglab: test=random seed=29
    FAIL test=random seed=29 cycle=1820: SCOREBOARD: RAM word 542 (address 00001878) read a82c9528, expected 00000000 (last written by DMA run #8)

    The tracker already holds two open bugs in this corner: long DMA runs that end early, and the FILL register that lost its reset. Is seed 29 one of those, or a third bug? The meeting is in twenty minutes, and you have time to set up one experiment. Which one?

    What people usually do, and why it fails

    They run whatever is nearest. Rerun the command (it fails the same way, as every deterministic run does). Try seed 30. Open a waveform and scroll. Or they pick the one suspect they like and test only that: "let me check whether run #8 was long". If the answer is no, three suspects are left and the meeting starts.

    None of those is wrong, but none was chosen. Guessing vs searching showed that the best yes-or-no run halves the suspects. Here the suspects are not a range you can bisect; they are four different mechanisms. So you need a different tool to find the halving run: a table.

    The method: tabulate, then choose

    1. Write the suspects as mechanisms. Three to five, each specific enough to predict something (see a hypothesis must predict). Include "the bench is wrong" when it is plausible.

    2. List candidate runs, including cheap ones you would normally not think of: another simulator, another start-value seed, a known fix applied, an older commit.

    3. Fill every cell: what does this run show if that suspect is the cause? Same failure, a different value, X, PASS.

    4. Delete columns where every row agrees. They cannot change your mind.

    5. Weigh the rest. Group the suspects by predicted result. The run is worth most when the groups carry about equal probability. Then divide by cost: a one-second run beats a one-hour build of nearly equal worth.

    6. Check the suspects are exclusive. If two can be true at once, plan a second run that tests the survivor on its own.

    For seed 29 the four suspects are:

    Suspect

    Mechanism

    A: the counter wraps (open bug)

    an earlier long copy ended early, so word 542 kept stale data

    B: FILL has no reset (open bug)

    run #8 is a zero fill that wrote FILL's power-up value

    C: a new fill datapath bug

    the fill logic from commit 12 writes a wrong word (a logic bug, so the same word every run)

    D: a bench model bug

    the design is right and the scoreboard's expected value is wrong

    The picture: suspects against experiments

    What each candidate run would show under each suspect, filled in before running anything, with the real results in the bottom row. The accent column is the run chosen first.

    Read it column by column. Rerun is grey all the way down: every suspect predicts the same failure, so it can teach nothing. Three columns (other start values, Icarus, two-state) single out B from the other three. Only both known fixes splits two against two.

    Which split is best? Count probability, not suspects

    A yes-or-no run whose outcome you give probability p is worth H(p) bits, as in guessing vs searching:

    H(p)=−plog2​p−(1−p)log2​(1−p)

    If all four suspects were equally likely, the 2:2 column would win: 1 bit against 0.81. But they are not. Two of them are open bugs that already explain similar failures; two are new and unexplained. Say A and B are 0.4 each and C and D 0.1 each (a judgement, written down so it can be argued with):

    Run

    Splits

    Equal odds

    A, B likelier (0.4, 0.4, 0.1, 0.1)

    Cost

    Rerun

    nothing

    0 bits

    0 bits

    1 s

    Icarus, four-state

    B against A, C, D

    0.81 bits

    0.97 bits

    1 s

    Two-state Verilator

    B against A, C, D

    0.81 bits

    0.97 bits

    1 s

    Both known fixes applied

    A, B against C, D

    1.00 bit

    0.72 bits

    a patched copy and a rebuild

    With realistic odds the Icarus run is worth more and costs a second. The 2:2 run answers "known or new?", which matters less when known is already likely. So run Icarus first, and keep the fix build for whatever survives.

    Do it: two runs

    Run 1: the same seed in a four-state simulator

    Prediction, written before running: if B, the read value is X; if A, C or D, it is a82c9528 again, because none of them involves an unknown value.

    Terminal
    $ tb/run.sh random 29
    dbglab: test=random seed=29
    FAIL test=random seed=29 cycle=1820: SCOREBOARD: RAM word 542 (address 00001878) read xxxxxxxx, expected 00000000 (last written by DMA run #8)

    X. A, C and D each predicted a known value, so all three are out in one second, and the result also says why the XRAND value looked random: it was FILL's random start value. The cheaper columns agree, as they should, since they ask the same question: with +verilator+seed+7 the same read gives a8c3ed4a instead of a82c9528, and two-state Verilator passes.

    Run 2: test the survivor on its own

    One suspect left, but do not stop at elimination: a suspect nobody listed would also have survived. Test B directly with a prediction that could fail: with only the FILL fix applied, seed 29 passes with XRAND on.

    Terminal
    $ XRAND=1 tb/run.sh random 29 verilator      # a copy of HEAD with FILL in the reset block
    dbglab: test=random seed=29
    PASS test=random seed=29 cycles=18522 time=185216 ns

    It passes, in 18522 cycles, the same count as two-state Verilator at HEAD. Seed 29 is a duplicate of the FILL bug, and you walk into the meeting with that and the two commands that show it. For completeness: the 2:2 column, both fixes applied, also passes, and so does commit 12, which has fill mode but neither open bug.

    What the evidence says

    Two runs settled four suspects, because the first was chosen from a table rather than from habit. Note what the table did beyond choosing: it made you write that a rerun was worthless before doing it, and it turned "Icarus shows X, Verilator does not" from a tool curiosity into the deciding measurement.

    Traps

    Common pitfall

    Suspects that can both be true. Seed 51 fails the same way in the same regression, and Icarus shows X too, so it looks like another FILL duplicate. Apply only the FILL fix, though, and it still fails, later and with a known value, which is the counter bug:

    FAIL test=random seed=51 cycle=11722: SCOREBOARD: RAM word 322 (address 00001508) read 15d021ca, expected 00000000 (last written by DMA run #75)

    Convicting B did not acquit A. That is why run 2 tests the survivor on its own, and why keeping several hypotheses alive matters.

    Common pitfall

    A column you cannot fill. If you cannot say what a run would show under a suspect, the suspect is too vague or the run is. Sharpen one of them before counting the column.

    Common pitfall

    Forgetting the unlisted suspect. Elimination only works over the list you wrote. Always finish with a run that tests the survivor's own prediction.

    Try it: prune the suspects

    The tracker below holds two real dbglab failures, each with the suspects a team would name in the first five minutes. Each experiment button shows its cost and how many groups it would split the remaining suspects into. Aim for one suspect left at the lowest cost, then check that the last experiment you ran changed the design and passed: crossing out the others is not the same as proving the survivor.

    Hypothesis tracker

    Loading hypothesis tracker…

    An experiment on which every remaining suspect agrees costs time and teaches nothing. Choose the cheap one they disagree on.

    Why this matters

    Senior lens. Make splitting runs cheap before you need them. dbglab's XRAND=1 switch, a second simulator in the same script, and known fixes kept as small patches you can apply with one command turn a day of setup into seconds. A regression that can be rerun "with all open fixes applied" answers the most common triage question, is this a duplicate?, by itself.

    When several people chase one failure, give each a column of the table, not a suspect. People defend the suspect they were given; a column is just a measurement.

    Interview

    Asked: "You have four possible causes for a failure and time for one experiment. How do you choose it?"

    Weak answer: I would test the most likely cause first, and if that is not it, move on to the next one.

    Strong answer: I would write the four as mechanisms and, for each experiment I can think of, what it would show under each cause. Columns where they all agree go. Of the rest I pick the one that splits the probability most evenly, adjusted for cost, which is often a cheap change of simulator, seed or configuration rather than a code change. Then I test the survivor directly, because elimination only covers the causes I listed, and I check whether two causes could both be present.

    Follow-ups: (1) How do you assign probabilities without fooling yourself? (2) What if the best experiment takes a day?

    Check yourself You add a fifth suspect, E: "the RAM model drops writes when the CPU and the DMA alternate quickly". Fill in its row for the five columns.

    Rerun: same. Other start values: same (no unknown values involved). Icarus: the same known value, not X. Two-state Verilator: FAIL. Both known fixes: FAIL. So E sits with C and D in every column, and the Icarus run eliminates it as well. A new suspect costs nothing to add to the table, and you can see at once which existing run already answers it.

    Check yourself A colleague argues for the 2:2 run because "it always gives one full bit". When are they right?

    When the four suspects really are about equally likely, for example when nothing like this has been seen before, and when the 2:2 run is cheap. Here two suspects were open bugs that already explained similar failures, so a run separating those from each other was worth more, and it cost a second instead of a rebuild.

    Remember

    • Fill in the table first: one row per suspect, one column per candidate run, the predicted result in each cell. A column with one value everywhere is worth nothing.

    • Split the probability, not the count. If two suspects are far likelier, the run that separates those two often beats a 2:2 split.

    • Suspects may both be true. A result that convicts one does not acquit the other.

    Rule of thumb: fill in the suspects-by-outcomes table before you run anything, then run the column that splits it best per minute.

    How this was checked

    dbglab at HEAD 49c5f04 (rebuilt with dbglab/build-history.sh), 2026-10-17. Report: XRAND=1 tb/run.sh random 29 verilator (Verilator 5.048, --x-initial unique, start-value seed 29) fails at cycle 1820 reading a82c9528; with +verilator+seed+7 appended it reads a8c3ed4a, with +verilator+seed+8 9d9758b2. tb/run.sh random 29 (Icarus 13.0) fails at cycle 1820 with xxxxxxxx; tb/run.sh random 29 verilator (two-state) passes in 18522 cycles.

    Fix builds (copies of HEAD): with both open fixes (FILL in the reset block, CW = 10) seed 29 passes with XRAND=1; with the FILL fix alone it passes; with CW = 10 alone it fails as at HEAD. Commit 12 (bc2bb34, a worktree) passes seed 29 in both modes. Seed 51 with the FILL fix alone and XRAND=1 fails at cycle 11722 reading 15d021ca.

    The information figures are computed from the probabilities stated, not measured. Limits: one failure, four suspects chosen by hand; a fifth mechanism nobody listed would survive every run here.

    Keep going

    • Practice: Counter with wrap and saturate modes

    • Practice: Reset strategies in RTL

    • Interview: run a mock interview on this topic

    Related topics

    • Part 4 · Hypothesise and Test

    • Guessing vs searching: information per experiment

    • X in a waveform: two-state against four-state simulation

    • Next: keeping several hypotheses alive

    Previous

    35 / 107

    Next: Keeping Several Hypotheses Alive

    Up next

    Up nextKeeping Several Hypotheses Alive

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Confirmation Bias and Blaming the Tool
    • One Change at a Time
    • The Debug Log