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
  • How to Read the Diagrams
  • Part 0: What Is a Protocol?10
  • Part 1: Timing and Clocking11
  • Part 2: Encoding and Framing12
  • Part 3: Handshakes13
  • Part 4: Flow Control17
  • Part 5: Transactions19
  • Part 6: Concurrency and Performance17
  • Part 7: Ordering13
  • Part 8: Sharing and Arbitration12
  • Part 9: Deadlock and Liveness13
  • Part 10: Reliability and Recovery20
  • Part 11: Bring-Up and Link Management10
  • Part 12: Power8
  • Part 13: Shared State and Coherence9
  • Part 14: Security and Isolation7
  • Part 15: Specifying and Verifying11
    • Writing an Interface Contract
    • Safety and Liveness
    • Protocol Assertions
    • Monitors and Transaction Reconstruction
    • Ordering-Aware Scoreboards
    • Protocol Coverage
    • Formal Methods for Protocols
    • Compliance and VIP
    • Observability and Tracing
    • A Waveform Debug Method
    • Drills: Specifying and Verifying
  • Part 16: Protocols Dissected17
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 15: Specifying and Verifying
  1. Topics
  2. First Principles of Hardware Protocols
  3. Part 15: Specifying and Verifying
  4. A Waveform Debug Method

Part 15 · Specifying and Verifying · Working (W)

A Waveform Debug Method

A repeatable method for debugging stalls, hangs and corruption on a waveform: find the stuck handshake, ask why each side waits, follow the dependency to the state that is wrong, and check it against a conservation rule. Applied to a simulated credit hang.

On this page

    Note

    Level: Working (W) · Answers: How do we know it is correct?

    Before this: Hang Debug, Credit Return Latency and Batching

    A simulation stops making progress. The waveform has three hundred signals and a hundred thousand cycles. An engineer who scrolls around looking for something odd may find it in an hour or a day. An engineer with a method asks four questions in order and usually finds the cause in minutes, because every hang and stall has the same shape: something is waiting for something else that will never happen.

    What is a reliable method for finding the cause of a stall, hang or corruption on a waveform?

    The obvious answer, and why it breaks

    The obvious approach is to start where the symptom was reported and look for anything unusual. In a large design, almost everything looks unusual to someone who did not write it, and the symptom is usually far from the cause, in both time and space. Without a method, debug follows curiosity rather than dependencies.

    The idea: follow the wait

    Flowchart: find the stuck handshake, where valid is high but the transfer never happens. Ask which condition is missing: ready, credit or grant. Find the state that drives that condition: a count, flag or FSM. Check whether that state is right using a conservation rule. If not, go back to the first cycle it went wrong; the root cause is the logic that updated it in that cycle. If it is right, the condition is waiting on something else: repeat.
    Start at the stuck handshake, ask which enabling condition is missing, find the state that drives it, check that state against what it should be, and walk back to the first cycle it diverged.

    Symptom

    First question

    Typical cause

    Hang

    Which handshake is stuck, and what is it waiting for?

    Lost credit or response, dependency cycle (Part 9), missing wake

    Stall (slow, not stopped)

    Which condition is false most of the time?

    Credits too few for the round trip, arbitration starvation, bubbles

    Corruption

    At which interface does the data first differ from the model?

    Data changed while stalled, wrong ID matching, overflow

    Build it: a credit hang

    A sender starts with 3 credits. The receiver returns credits in batches worth 4, once it has drained 4 entries. The design hangs; apply the method.

    tb.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    // A stuck valid, for the debug method. A sender starts with 3 credits (its
    // configured initial value) and sends whenever it has one. The receiver drains one
    // entry per cycle but returns credits in batches: one crd_ret pulse is worth 4
    // credits, sent once it has drained 4 entries since its last return. With only 3
    // credits in the loop, the receiver never reaches 4: the sender has data (vld high)
    // and no credits, the buffer is empty, and 3 drained credits sit in the receiver
    // waiting for a fourth that cannot come. The checker confirms the permanent hang.
    module tb;
      logic clk = 0;
      always #5 clk = ~clk;
      initial begin $dumpfile("wave.vcd"); $dumpvars(0, tb); end
    
      int cyc = 0;
      always @(posedge clk) cyc <= cyc + 1;
    
      localparam int BATCH = 4;
      logic       vld = 0, crd_ret = 0;
      logic [2:0] credits = 3, occ = 0, owed = 0;    // owed: drained, not yet returned
      logic       send, pop;
      always_comb begin
        send = vld && credits != 0;
        pop  = occ != 0;
      end
      always @(posedge clk) begin
        if (cyc >= 1) vld <= 1;                      // the sender always has data
        occ <= occ + send - pop;
        // Receiver: return a batch of credits once BATCH have been drained
        crd_ret <= 0;
        if (owed + pop == BATCH) begin crd_ret <= 1; owed <= 0; end
        else owed <= owed + pop;
        // Sender
        credits <= credits - send + (crd_ret ? BATCH : 0);
      end
    
      int stuck = 0;
      always @(posedge clk) if (vld && !send) stuck++;
      initial begin
        #250;
        if (stuck > 10 && credits == 0 && occ == 0 && owed == 3)
          $display("PASS: valid stuck for %0d cycles: no credits, empty buffer, 3 credits owed but held for a batch of 4", stuck);
        else $display("FAIL: stuck %0d credits %0d occ %0d owed %0d", stuck, credits, occ, owed);
        $finish;
      end
    endmodule

    Watch it run

    Applying the method, slot by slot:

    • Find the stuck handshake: from slot 4 on, vld is high but send is 0, for ever. Slots 1 to 3 show the only three sends.

    • Which condition is missing? send needs a credit; credits reaches 0 in slot 4 and never recovers.

    • What drives credits? Sends (down) and crd_ret (up). crd_ret never pulses in the whole trace.

    • Is the receiver's state right? occ returns to 0 in slot 5: the buffer is empty. owed climbs 1, 2, 3 in slots 3 to 5 and stops. Conservation check: credits (0) + occupancy (0) + owed (3) = 3, the initial credits, so no credit was lost. All three are owed, held back waiting for a batch of 4 that can never form.

    • Root cause: the batch size (4) exceeds the credits in the loop (3). Fix: return credits when the batch is full or when the buffer drains, or make the batch no larger than the sender's initial credits.

    Habits that make the method fast

    • Put the handshake signals of every interface on one view before looking at anything else.

    • Use conservation rules: credits + occupancy + in flight = constant; requests issued − responses received = outstanding. A broken sum tells you a leak happened, and when.

    • Find the first divergence, not the most visible one: the cycle a count first goes wrong is where the bug is.

    • Let assertions and monitors do the searching: a failing assertion (lesson 15.3) is the method's first step done for you.

    How real tools support it

    Aid

    Helps with

    Assertions with rule names

    Pointing at the cycle and rule that failed

    Transaction views from monitors

    Seeing packets and responses, not just bits

    Silicon trace buffers frozen by a timeout

    The same method in silicon (lesson 15.9)

    What breaks

    The batching hang (illustrative) appeared only after a configuration change lowered the sender's initial credits from 4 to 3 to save area. The receiver's batch size was a separate parameter in another block. An assertion that the batch size never exceeds the sender's initial credits, added to the configuration checks, prevented it from recurring.

    Why this matters

    Senior lens. Debug is following dependencies, not looking for oddities. Teach the method to the team, build the waveform views and conservation checks that make it fast, and turn every root cause into an assertion so the same class of bug fails at its cause next time.

    Interview

    Asked: "Walk me through debugging a stuck valid."

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

    Strong answer: Valid stuck high means the transfer's enabling condition never comes true, so I find which one: ready, a credit, a grant. Then I find the state that drives it, a FIFO count, a credit counter, an arbiter, and check it against what it should be, using a conservation rule like credits plus occupancy plus credits in flight equals the buffer depth. If the state is wrong, I go back to the first cycle it diverged and look at the logic that updated it then. If it's right, that condition is itself waiting on something, and I repeat. Typical answers: a lost or withheld credit, a dependency cycle, a response that never came.

    Follow-ups: (1) What conservation rule would you use for a request/response interface? (2) How do you find the first divergence quickly?

    Check yourself In the waveform, how do you know no credit was lost, only withheld?

    Credits (0) + occupancy (0) + owed (3) still equals the initial 3: all three are held by the receiver.

    Check yourself Name a fix for the batching hang that keeps batching for efficiency.

    Return the owed credits when the buffer drains (or after a timeout), even if the batch is not full.

    Key takeaways

    • Every hang is something waiting for something that will never happen: follow the wait.

    • Find the stuck handshake, the missing condition, the state behind it, and the first cycle that state went wrong.

    • Conservation rules expose leaks; turn each root cause into an assertion.

    Keep going

    Next: Drills: Specifying and Verifying. Interview questions, a whiteboard exercise and a ten-question checkpoint for Part 15.

    Related topics

    • Part 15 overview: Specifying and Verifying

    Previous

    205 / 234

    Next: Drills: Specifying and Verifying

    Up next

    Up nextDrills: Specifying and Verifying

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Writing an Interface Contract
    • Safety and Liveness
    • Protocol Assertions
    clkvldsendcredits3210occ010owed0123crd_ret
    Simulation output: the sender sends three times, runs out of credits, and stops for good; the buffer drains to empty while the receiver holds 3 owed credits, waiting for a fourth before it returns any.