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 Path
  • CDC/RDC Glossary
  • CDC/RDC Metrics Reference
  • CDC/RDC Interview Prep
  • CDC/RDC Signoff Cheatsheet
  • Clock Domains and Failure Modes8
  • Metastability and Synchronizers16
  • CDC Protocols and Handshakes16
  • Reconvergence and Gray Coding16
  • Reset Domain Crossing16
  • Architecture and Design Rules16
  • CDC Signoff Methodology24
  • RDC Signoff16
  • Debug and Case Studies11
    • Reading a CDC Report
    • Classic Bug Anatomy
      • Lost Pulse Bug
      • Torn Bus Bug
      • FIFO Pointer Bug
      • Reset Release Bug
    • Case Studies
  • CDC and RDC Interview Prep30
  • Capstone: Sign Off a Multi-clock Peripheral4
Loading lesson

‹Classic Bug Anatomy
  1. Topics
  2. CDC & RDC
  3. Debug and Case Studies
  4. Classic Bug Anatomy
  5. Lost Pulse Bug

Part 9 · Debug and Case Studies · Advanced

Lost Pulse Bug

Events that vanish crossing into a slower domain: the spacing rule, what cancellation looks like, and the guard that proves it cannot recur.

On this page

    The symptom is a count that never adds up: interrupts or credits sent from a fast domain arrive fewer times than sent, rarely and irreproducibly. Simulation passes because the test never places two events close together; the lab fails whenever real traffic does. The mechanism is almost always spacing: a toggle scheme re-toggled before the destination could absorb the first event.

    What the waveform shows

    The simulation output below drives three source events through a toggle synchroniser into a domain four times slower. The first two events are 20 ns apart, inside one 40 ns destination window; the third stands alone.

    How to read it: pulse_s carries three one-cycle source events and toggle_s flips once per event. q1 and sync_t are the destination synchroniser stages and pulse_d is the edge-detected output. The flips at the first two events cancel inside a single destination sampling window, so sync_t never moves and pulse_d stays silent: two events in, nothing out. The third flip stands alone in its window, sync_t rises two slow edges later, and pulse_d fires once.

    Root cause

    A toggle scheme carries each event as a level change and needs each level sampled at least once before the next change. Re-toggling inside one destination sampling window returns the line to its old value before any sampling edge sees the new one, and the destination cannot distinguish that from no event at all. The general rule is generous spacing: each toggle level must stand for enough destination cycles to cross the synchroniser and be consumed, so the source must rate-limit events or hold off on a busy indication. Short pulses without any scheme are worse: a pulse narrower than the destination sampling window may never overlap a sampling edge at all.

    The finding that would have caught it

    Structural analysis flags pulse crossings without a recognised scheme or without a spacing constraint, and the spacing lesson of the pulse topic states the minimum-gap rule the review should have demanded. A report entry of unspaced pulse crossing plus a testbench that never emits back-to-back events is the full signature of this bug waiting to happen.

    Fix and regression guard

    Fix it at the source: enforce the minimum gap with a busy flag or acknowledge, or replace the toggle with a handshake when the event rate cannot be bounded. The guard has two halves that last: an assertion bounding the minimum spacing between source events, and a self-checking test that emits events at the minimum legal gap and counts receipts. The testbench below is that second half for an honestly spaced design, and it prints PASS:

    sync2.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
    module sync2 (
      input  logic clk,
      input  logic d,
      output logic q
    );
      logic q1 = 1'b0, q2 = 1'b0;
      always @(posedge clk) begin
        q1 <= d;
        q2 <= q1;
      end
      assign q = q2;
    endmodule
    
    module tb;
      reg clk_s = 0;
      always #5 clk_s = ~clk_s;
      reg clk_d = 0;
      always #15 clk_d = ~clk_d;
      reg toggle_s = 1'b0;
      wire sync_t;
      logic sync_t_d = 1'b0;
      wire pulse_d;
      integer received = 0;
      sync2 u_tog (.clk(clk_d), .d(toggle_s), .q(sync_t));
      always @(posedge clk_d) sync_t_d <= sync_t;
      assign pulse_d = sync_t ^ sync_t_d;
      always @(posedge clk_d) if (pulse_d) received = received + 1;
      initial begin
        #26 toggle_s = 1'b1;
        #120 toggle_s = 1'b0;
        #120 toggle_s = 1'b1;
        #150;
        if (received == 3) $display("PASS: 3 sent, 3 received");
        else $display("FAIL: 3 sent, %0d received", received);
        $finish;
      end
    endmodule

    Common pitfall

    Proving the spaced design works does not prove the spacing is enforced. The testbench above passes by construction; without the spacing assertion on the real source logic, a later change can shrink the gap and the passing test keeps passing while the bug returns.

    Check yourself Two events 20 ns apart enter a toggle synchroniser whose destination period is 40 ns. How many events can the destination report, at most?

    Zero: both flips can fall inside one sampling window and cancel, so the destination may see no change at all.

    Check yourself The fix enforces spacing in the source. Name the two regression halves and what each one proves.

    A spacing assertion proves the source cannot emit events closer than the rule allows; a counting testbench proves legally spaced events are all received exactly once.

    Related topics

    • Toggle Synchronizer

    • Spacing and Rate Limits

    • Assertions for Protocols

    Previous

    139 / 179

    Next: Torn Bus Bug

    Up next

    Up nextTorn Bus Bug

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • FIFO Pointer Bug
    • Reset Release Bug
    clk_spulse_stoggle_sclk_dq1sync_tpulse_d
    Simulation output: two closely spaced toggles cancelling inside one destination window so no event is received, then a well-spaced toggle received cleanly