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

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • Meet DCRouter
  • Glossary
  • Part 0: What Verification Is8
  • Part 1: What Can Go Wrong11
  • Part 2: Specifications as Checkable Rules8
  • Part 3: Observability and Controllability7
  • Part 4: Stimulus11
  • Part 5: Checking11
  • Part 6: Coverage10
  • Part 7: Formal and Simulation10
  • Part 8: Verification Levels and Reuse8
  • Part 9: Planning and Risk9
  • Part 10: Emulation and Prototyping8
  • Part 11: Post-Silicon Validation8
  • Part 12: Bug Economics8
    • The Cost of Bugs by Stage
    • Bug-Discovery Curves
    • Diminishing Returns
    • Triage and Severity
    • Root Cause and Fix Quality
    • Estimating Remaining Bugs
    • Investing in Infrastructure
    • Drills: Bug Economics
  • Part 13: Debug7
  • Part 14: Sign-Off7
  • Part 15: Verification in Context8
  • Part 16: Case Studies5
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 12: Bug Economics
  1. Topics
  2. First Principles of Verification Strategy
  3. Part 12: Bug Economics
  4. Root Cause and Fix Quality

Part 12 · Bug Economics · Senior (S)

Root Cause and Fix Quality

A fix that makes the failing test pass is not necessarily a fix. Patches that treat a symptom leave the cause in place, and every change can introduce new bugs. How to find the root cause before fixing, how to verify a fix (the failing test, the rule, its neighbours, the regression), and two plausible-looking fixes for DCRouter bugs that would have been wrong.

On this page

    Note

    Level: Senior (S) · Answers: How do we spend verification effort wisely?

    Before this: Triage and Severity

    BUG_1 makes a DCRouter FIFO's full flag rise one entry late, so a ninth word overwrites the oldest. The failing test shows corrupted flits at an output. A quick fix stops the corruption: block the push when the count equals 8, inside the push logic. The test passes. The full flag is still wrong, in_rdy still goes low one push too late, and the next test that relies on in_rdy to throttle a source fails in a different, more confusing way. The symptom was fixed; the cause was not.

    How do you verify a fix?

    The obvious answer, and why it breaks

    The obvious check is that the failing test now passes. That shows only that this one stimulus no longer triggers this one symptom. A fix can mask the symptom, move the bug elsewhere, or break something nearby, and the failing test is blind to all three. Fixes are design changes, and they deserve the same scrutiny as any other change (lesson 9.7).

    The idea: root cause first, then verify the fix in four ways

    Step

    Question

    Root cause

    Which line, which rule, which assumption is wrong, and why did it happen?

    Fix at the cause

    Does the change correct the rule's implementation, not just the symptom?

    1. The failing test

    Does it now pass, for the right reason (check the waveform, not just the verdict)?

    2. The rule

    Is the rule now true in every case: an assertion or formal property for it?

    3. The neighbours

    What else uses the changed logic? Tests and checks for those paths

    4. The regression

    Does everything that passed before still pass?

    Sweep

    Is the same mistake elsewhere? (lesson 11.7)

    Two plausible wrong fixes for DCRouter bugs

    design.sv
    1
    2
    3
    4
    5
    6
    // snippet: BUG_1, symptom fix (wrong) and root-cause fix (right)
    // wrong: refuse the ninth push inside the FIFO; the FIFO stays consistent, but full is
    // still late, so in_rdy tells the source its flit was accepted and the flit is dropped
    wire push_ok = push && (count != 4'd8);      // used for the write, the pointer and the count
    // right: fix the full flag itself, which in_rdy and push both depend on
    assign full = (count == 4'd8);

    The wrong fix ends the corruption and even satisfies the formal property "count never exceeds 8". But the source sees in_rdy high, believes its ninth flit was accepted, and moves on: the scoreboard now reports a lost flit instead of a corrupted one. The rule broken was R1 (in_rdy low when full); only fixing full itself makes it true for every consumer.

    design.sv
    1
    2
    3
    4
    5
    // snippet: BUG_9, symptom fix (wrong) and root-cause fix (right)
    // wrong: never send in a cycle where a credit returns, so the buggy counter never sees both
    send[o] = lock[o] && !empty[g] && crd_ok && !sleeping && !out_crd[o];
    // right: count a send and a return in the same cycle, as R7 says
    credits[o] <= credits[o] + (out_crd[o] ? 3'd1 : 3'd0) - (send[o] ? 3'd1 : 3'd0);

    The wrong fix is the more instructive one. Credits are now conserved: every functional test passes and the formal conservation property (lesson 7.9) passes too. But under streaming traffic a credit returns almost every cycle, so the output stops sending in those cycles and its throughput can halve. No functional check sees it; only a throughput check (lesson 5.9, feature F26 in lesson 9.8) does. A symptom fix can satisfy the rule it was aimed at while breaking a different one.

    Fix quality signals

    • The fix is in the logic the rule describes, not in a downstream guard.

    • The explanation is complete: it accounts for every observed symptom.

    • A property or assertion for the rule passes, for all inputs if formal applies.

    • Neighbouring paths are re-verified, not just the failing one.

    • The fix is small: a large fix for a small bug is a warning sign.

    How real teams verify fixes

    Practice

    Purpose

    Root-cause analysis recorded with every fix

    The cause, not just the change, is reviewed

    A permanent regression test plus a rule check per fixed bug

    This bug and its relatives stay fixed

    Fix reviews by someone other than the author

    Symptom patches caught before they land

    Fix-induced bug rate tracked

    Late fixes judged by how often they break something

    What breaks

    Late, rushed fixes are a familiar source of new bugs. A team (illustrative) fixed a deadlock by adding a timeout that released the lock after 1,000 cycles. The deadlock test passed. The timeout also fired during legitimate long transfers, corrupting them; the real cause, an ordering error in the request logic, remained.

    Why this matters

    Senior lens. Never accept "the test passes now" as fix verification. Require the root cause in writing, a check of the rule itself (ideally a proof), re-verification of the neighbouring paths, the regression, and a sweep for the same mistake. A symptom patch is a new bug waiting for its test.

    Interview

    Asked: "How do you verify a fix?"

    Weak answer: Rerun the failing test and check it passes.

    Strong answer: Root cause first, then four checks. The failing test passes for the right reason, confirmed in the waveform. The rule itself holds in every case, with an assertion or a formal property, because a symptom patch can pass the test and still break the rule, and a related rule such as throughput can break too. Neighbouring paths that use the changed logic are re-verified. And the whole regression still passes. Then I sweep for the same mistake elsewhere. For example, guarding a FIFO write instead of fixing a late full flag makes the corruption test pass but leaves in_rdy lying to the source.

    Follow-ups: (1) What makes a fix suspicious? (2) Why check the rule and not just the test?

    Check yourself Why is guarding the FIFO write a symptom fix for BUG_1?

    The cause is the full flag; in_rdy depends on it too, so the source is still told the FIFO can accept a word when it cannot.

    Check yourself Which check would expose the wrong BUG_9 fix, and why do the functional checks miss it?

    A throughput check under streaming traffic: the wrong fix conserves credits, so functional checks and the conservation proof pass, but it stops sending whenever a credit returns.

    Key takeaways

    • A passing failing-test does not verify a fix.

    • Find the root cause, fix it there, and check the rule, the neighbours and the regression.

    • Treat fixes as changes: review them, track the bugs they introduce, and sweep for the same mistake.

    Keep going

    Next: Lesson 12.6, Estimating Remaining Bugs. How many bugs are left?

    Related topics

    • Part 12 overview: Bug Economics

    Previous

    119 / 160

    Next: Estimating Remaining Bugs

    Up next

    Up nextEstimating Remaining Bugs

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • The Cost of Bugs by Stage
    • Bug-Discovery Curves
    • Diminishing Returns