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
  • Part 13: Debug7
    • The Debug Loop
    • Reproducing Failures
    • Following the Wait
    • Bisection and Delta Debugging
    • Triage at Scale
    • Is It the Testbench?
    • Drills: Debug
  • Part 14: Sign-Off7
  • Part 15: Verification in Context8
  • Part 16: Case Studies5
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 13: Debug
  1. Topics
  2. First Principles of Verification Strategy
  3. Part 13: Debug
  4. Following the Wait

Part 13 · Debug · Working (W)

Following the Wait

Hangs and stalls have a reliable debug method: find the signal that is waiting, ask what it is waiting for, and follow that condition backwards until you reach the logic that should have released it. A runnable DCRouter hang (Bug Zoo BUG_21) and its waveform, followed from a stuck valid on input 3 to the one line of RTL that ignores it.

On this page

    Note

    Level: Working (W) · Answers: How do we find the cause fast?

    Before this: Reproducing Failures, Meet DCRouter

    A test reports that traffic stopped: flits are still pending after thousands of cycles. There is no corrupted data and no assertion message, only silence. Hangs are intimidating because nothing points at the cause. They are also the most systematic failures to debug, because every hang has something waiting, and every wait has a condition that never became true.

    Walk me through a stuck valid.

    The obvious answer, and why it breaks

    The obvious approach scrolls through the waveform looking for something odd. In a long simulation with many signals, everything looks odd and nothing points anywhere. The method is to start from the one thing known to be stuck and ask a single question repeatedly: what is it waiting for?

    The idea: follow the wait backwards

    Step

    Question

    1. Find the waiting handshake

    Which valid is high and has been high too long?

    2. Why is it not accepted?

    Which condition of ready is false?

    3. Why is that condition false?

    Look up its logic; which input to it is holding it false?

    4. Repeat

    Follow that input to its own logic, until you reach state that should have changed

    5. Why did that state not change?

    The logic that should have changed it is the bug, or its input never came

    Build it: a DCRouter hang

    The testbench builds DCRouter with Bug Zoo BUG_21, sends one packet, lets the router fall asleep, then offers a packet on input 3 alone, and records what the pins show.

    tb.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // uses: dcrouter BUG_21
    // Lesson 13.3: a hang to debug. DCRouter is built with Bug Zoo BUG_21. One packet crosses the
    // router, the router falls asleep after the idle threshold, then input 3 alone offers a
    // packet. The test records what the pins show for 40 cycles and passes when the hang is
    // reproduced exactly as reported: input 3 holding valid, in_rdy low, the router asleep.
    module tb;
      `include "dcr_env.svh"
      int held = 0;
      initial begin
        reset_dut();
        queue_pkt(0, 1, 0, 2);
        wait_drain();
        repeat (12) @(posedge clk);                   // idle long enough to sleep
        queue_pkt(3, 2, 0, 2);                        // input 3 alone offers a packet
        repeat (40) @(posedge clk) if (in_vld[3] && !in_rdy[3] && sleeping) held++;
        if (held >= 38) $display("PASS: hang reproduced: input 3 held valid for %0d of 40 cycles with in_rdy low while the router stayed asleep", held);
        else $display("FAIL: no hang (input 3 stalled for %0d cycles)", held);
        $finish;
      end
    endmodule

    It prints:

    Diagram
    PASS: hang reproduced: input 3 held valid for 40 of 40 cycles with in_rdy low while the router stayed asleep

    Following the wait

    Step

    Observation

    Next question

    1

    in_vld = 8: input 3 holds valid from slot 15 to the end

    Why is it never accepted?

    2

    in_rdy = 0 for every input

    in_rdy[i] = !full && port_en && !sleeping: which term is false?

    3

    FIFOs are empty and ports are enabled; sleeping = 1

    Why does the router stay asleep?

    4

    sleeping clears only when wake is true

    What drives wake?

    5

    The RTL computes wake from in_vld

    Read it:

    design.sv
    1
    2
    3
    4
    5
    6
    logic wake;
    `ifdef BUG_21
      assign wake = |in_vld[2:0];                                // BUG_21: port 3 cannot wake it
    `else
      assign wake = |in_vld;
    `endif

    With BUG_21 defined, wake looks only at in_vld[2:0]. Input 3 can never wake the router. Five questions from a silent hang to one line of RTL, without reading any logic that was not on the path. In this course's testbenches, the R13 wake assertion (lesson 5.3) reports the same thing at the cycle it happens, which is exactly why such assertions are worth writing.

    The method for corruption, too

    • For wrong data, the same method runs backwards from the wrong value: where did this value come from, one register stage at a time, until the stage where right became wrong.

    • For lost data, follow the item: where was it last seen, and which handshake failed to pass it on?

    • In all cases, follow one signal's dependencies, not the whole waveform.

    How real teams debug hangs

    Practice

    Purpose

    Watchdogs that report which handshakes are stalled at timeout

    The starting point of the wait is printed, not searched for

    Assertions on progress ("valid eventually accepted within N cycles")

    Hangs reported where and when they begin

    Waveform tools that trace a signal's drivers

    Each "why" answered with one click

    What breaks

    A team (illustrative) debugged a system hang by inspecting hundreds of signals in the last thousand cycles of a long simulation. The bus that stalled first had stopped 200,000 cycles earlier; everything near the end was a consequence. A watchdog printing the oldest stalled handshake would have pointed straight at it.

    Why this matters

    Senior lens. Teach and use the wait-following method for every hang: start at the stuck handshake, ask what it waits for, follow one dependency at a time. Then make it unnecessary next time by adding progress assertions and watchdogs that name the oldest stalled handshake.

    Interview

    Asked: "Walk me through a stuck valid."

    Weak answer: I'd look through the waveform for anything unusual.

    Strong answer: I start from the stuck handshake and follow the wait. Valid is high, so why is ready low? I look up ready's logic and find which term is false. Then why is that term false? I follow it to its driver, and repeat until I reach state that should have changed and didn't. On the course's router, input 3's valid was stuck because in_rdy was low because the router was asleep, because wake never rose, because the wake logic ignored input 3: five questions to one line. Then I'd add a progress assertion so the next hang reports itself.

    Follow-ups: (1) How would you do the same for corrupted data? (2) What is a progress assertion?

    Check yourself In the waveform, which term of in_rdy is holding it low after slot 6?

    sleeping: the FIFOs are empty and the ports enabled, but the router is asleep.

    Check yourself What assertion would have reported this hang at the cycle it began?

    R13's wake rule: if sleeping and any in_vld is high, sleeping must be low in the next cycle.

    Key takeaways

    • Every hang has something waiting; follow what it waits for, one dependency at a time.

    • For corruption, follow the wrong value backwards stage by stage.

    • Progress assertions and watchdogs turn hangs into precise reports.

    Keep going

    Next: Lesson 13.4, Bisection and Delta Debugging. Shrinking the failing case and the change set.

    Related topics

    • Part 13 overview: Debug

    Previous

    125 / 160

    Next: Bisection and Delta Debugging

    Up next

    Up nextBisection and Delta Debugging

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • The Debug Loop
    • Triage at Scale
    • Is It the Testbench?
    clkin_vld08in_rdyf0sleepingout_vld0
    Simulation output (BUG_21 build): sleeping rises and in_rdy falls to 0; nine cycles later in_vld becomes 8 (input 3) and stays there; in_rdy stays 0 and sleeping stays 1 for the rest of the window.