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

  • RTL Foundations36
  • Combinational Design25
  • Sequential Design27
    • Flip-Flops and Registers
      • Edge-Triggered State Elements
      • D Flip-Flop with Asynchronous Reset
      • Synchronous Reset and Register Enable
      • Clock Enable vs Gated Clock
      • A Hold Violation, Worked
    • Counters and Timers
    • Shift Registers and LFSRs
    • Pipelining
    • Stalls, Bubbles and Hazards
    • Retiming
  • FSM & Control27
  • Timing & Clocking39
  • Reset & CDC34
  • Memory & Buses57
  • Advanced RTL28
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

‹Flip-Flops and Registers
  1. Topics
  2. RTL Design
  3. Sequential Design
  4. Flip-Flops and Registers
  5. A Hold Violation, Worked

Part 3 · Sequential · Intermediate

A Hold Violation, Worked

Two flops, a 10 ps wire and 50 ps of clock skew: a hold failure with numbers, a simulation and the fix.

On this page

    Why this matters

    A bare wire between two flops can fail hold by 20 ps when the capture clock is 50 ps late, and the part then fails at every clock frequency. Slowing the clock, the usual cure for timing trouble, does nothing.

    After this lesson you can: compute hold slack from clock-to-Q, wire delay, hold time and skew, place the arrival on a timeline, size a fix, and explain recovery and removal for a reset release.

    New data arrives before the old data is safe

    Two flops share one clock and are joined by a bare wire, the link of a shift register. The capture flop's clock arrives 50 ps late, and a simulation with a hold check prints HOLD VIOLATION ... d changed 10 ps after the clock edge ..., hold is 30 ps on every cycle.

    Hold is the other half of the flop's sampling window. After the edge, D must stay still for t_hold so the flop can finish taking the old value. Here the next value races through so fast that it lands inside that window.

    Time measured from the launch edge. The capture edge is 50 ps later, its 30 ps hold window runs from 50 to 80 ps, and new data arrives at 60 ps, inside it. With 40 ps of buffer the data arrives at 100 ps, after the window.

    Read each data row as the moment the next value reaches flop B's D pin. It must land to the right of the shaded band.

    • Launch: flop A's clock rises at 0 ps. Its Q changes 50 ps later and the wire adds 10 ps, so B's D pin moves at 60 ps.

    • Capture: B's clock rises at 50 ps, so B must hold its old D value until 50 + 30 = 80 ps.

    • Top data row: the change at 60 ps is 20 ps too early. B may capture the new value, a mix, or go metastable.

    • Bottom data row: a 40 ps buffer moves the change to 100 ps, 20 ps clear of the window.

    The hold slack, term by term

    Hold compares the earliest arrival of new data with the time the old data is still needed, both measured from the same launch edge:

    hold slack=(tcq,min​+tlogic,min​)−(thold​+tskew​)

    Term

    Value

    Why this value

    t_cq,min

    50 ps

    Fastest clock-to-Q of flop A: hold uses the minimum

    t_logic,min

    10 ps

    A direct Q-to-D wire, no gates

    Arrival

    60 ps

    50 + 10

    t_hold

    30 ps

    B's hold requirement

    skew

    50 ps

    B's clock arrives 50 ps after A's (positive skew)

    Required

    80 ps

    30 + 50

    Hold slack

    -20 ps

    60 - 80: a violation by 20 ps

    Positive skew is what turns a harmless wire into a failure. With zero skew the same path has 60 - 30 = +30 ps of slack. The late capture clock pushes the end of the window 50 ps to the right while the data arrives at the same time.

    Why a slower clock does not help

    The clock period is not in the equation. Hold checks one edge against itself: the edge that launches the next value at A is the same edge whose window B must protect. Doubling the period moves the next edge further away, which helps setup, but the race between A's new value and B's window happens within the first 80 ps after every edge.

    The simulation shows this directly. The bench below runs the broken link at a 1000 ps period, then again at 2000 ps:

    Diagram
    $ iverilog -g2012 -s tb_hold -o h hold.sv && vvp h
    HOLD VIOLATION tb_hold.bad.ff_b: d changed 10 ps after the clock edge at 550 ps, hold is 30 ps
    HOLD VIOLATION tb_hold.bad.ff_b: d changed 10 ps after the clock edge at 1550 ps, hold is 30 ps
    ...
    violations: bad=7 buffered=0 deskewed=0; bad q_b unknown in 5 cycles
    PASS: hold check fires on the short skewed path and is silent after either fix
    
    $ iverilog -g2012 -s tb_hold -P tb_hold.HALF=2000 -o h4 hold.sv && vvp h4
    HOLD VIOLATION tb_hold.bad.ff_b: d changed 10 ps after the clock edge at 2050 ps, hold is 30 ps
    HOLD VIOLATION tb_hold.bad.ff_b: d changed 10 ps after the clock edge at 6050 ps, hold is 30 ps
    ...
    violations: bad=7 buffered=0 deskewed=0; bad q_b unknown in 5 cycles

    Read the log line by line:

    • Edge at 550 ps: the first rising edge is at 500 ps; B sees it 50 ps later. A's new Q reaches B at 560 ps, 10 ps after B's edge, inside the 30 ps window.

    • 2000 ps period: the edges move to 2050, 6050 ps and so on, but the gap is still 10 ps. The period changed and the violation did not.

    • Counts: the broken link reports 7 violations and its output goes unknown in 5 checked cycles. Both fixed links report 0 and shift the pattern correctly.

    RTL simulation normally checks none of this: a plain always_ff samples D at the edge in zero time and would shift the pattern correctly. The model below adds the two numbers that matter, a clock-to-Q delay and a hand-written hold check, and turns Q unknown on a violation, as a gate-level model does.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // snippet: the hold check inside dff_hold (full listing under Testbench)
      always @(posedge clk) begin
        t_edge = $time;
        q <= #TCQ d;                        // new Q appears TCQ after the edge
      end
      always @(d)                           // D moved: was it inside the hold window?
        if ($time - t_edge < THOLD) begin
          violations++;
          $display("HOLD VIOLATION %m: d changed %0d ps after the clock edge at %0d ps, hold is %0d ps",
                   $time - t_edge, t_edge, THOLD);
          q <= #TCQ 1'bx;                   // what a gate-level model does: Q becomes unknown
        end

    Two fixes, with the new slack

    Fix

    Arrival

    Required

    Hold slack

    Cost

    None

    50 + 10 = 60 ps

    30 + 50 = 80 ps

    -20 ps

    Fails at every clock period

    Add a 40 ps delay buffer on the wire

    50 + 10 + 40 = 100 ps

    80 ps

    +20 ps

    Area and power; setup slack on this path drops by 40 ps

    Cut skew from 50 to 10 ps

    60 ps

    30 + 10 = 40 ps

    +20 ps

    A clock-tree change, which moves other paths too

    The buffer costs setup slack on this path only, and a direct flop-to-flop link has almost a full period of setup margin to spare. That is why hold fixing inserts delay cells on short paths after the clock tree is built, when the real skew is known. The hold failure example in the timing section works a second case and lists every fix option; the slack calculator lets you move each term.

    Recovery and removal: hold for a reset release

    An asynchronous reset pin has the same window, applied to the moment reset is released. The flop must see the release either well before an edge or well after it. Release inside the window and the flop may leave reset on this edge, the next, or go metastable, while its neighbours choose differently.

    Check

    Data-pin twin

    Reset release must be

    Example numbers

    Recovery

    Setup

    At least t_rec before the clock edge

    t_rec = 40 ps: release at -60 ps passes, at -20 ps fails by 20 ps

    Removal

    Hold

    At least t_rem after the clock edge

    t_rem = 30 ps: release at +10 ps fails by 20 ps

    The fix is structural rather than a buffer: release the reset from a flop clocked by the same clock, so the release always lands a clock-to-Q after an edge and timing analysis can check it like any data path. That is the async-assert, sync-deassert pattern in the reset template lesson; recovery and removal covers how they are verified.

    Common mistakes

    Common pitfall

    Slowing the clock to cure a hold violation. The hold check compares the launch edge with the same capture edge, so the period never appears; the 2000 ps run above fails exactly like the 1000 ps run. Only a longer minimum path or less skew moves hold slack. A part that fails hold on the bench fails at every frequency.

    Check yourself A path has t_cq,min = 40 ps, a 15 ps wire, t_hold = 25 ps and 45 ps of positive skew. What is the hold slack, and how much delay must be added for +10 ps of margin?

    Arrival 40 + 15 = 55 ps; required 25 + 45 = 70 ps; slack -15 ps. For +10 ps the arrival must reach 80 ps, so add 25 ps of delay. Halving the clock frequency changes nothing.

    Check yourself Same path, but the clock tree is rebalanced so skew becomes -5 ps (the capture clock now arrives first). What is the hold slack, and what happened to setup?

    Required 25 - 5 = 20 ps; slack 55 - 20 = +35 ps. Setup loses the same 50 ps that hold gained, because an earlier capture edge leaves less time for the slow path.

    Testbench

    Three copies of the two-flop link run on one clock: the broken one (skew 50 ps, no buffer), one with a 40 ps buffer and one with skew cut to 10 ps. The bench passes when the broken link reports a hold violation, both fixed links report none, and both fixed links shift the pattern like a zero-delay model. Times are in picoseconds.

    dff_hold.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
    // Behavioural flop with clock-to-Q and a hand-written hold check (times in ps).
    module dff_hold #(parameter int TCQ = 50, THOLD = 30) (
      input  logic clk, d,
      output logic q
    );
      timeunit 1ps; timeprecision 1ps;
      time t_edge = -1000;                  // time of the last rising edge on this flop's clk pin
      int  violations = 0;
      always @(posedge clk) begin
        t_edge = $time;
        q <= #TCQ d;                        // new Q appears TCQ after the edge
      end
      always @(d)                           // D moved: was it inside the hold window?
        if ($time - t_edge < THOLD) begin
          violations++;
          $display("HOLD VIOLATION %m: d changed %0d ps after the clock edge at %0d ps, hold is %0d ps",
                   $time - t_edge, t_edge, THOLD);
          q <= #TCQ 1'bx;                   // what a gate-level model does: Q becomes unknown
        end
    endmodule
    
    // Two flops on one clock: A launches, B captures. B's clock is late by SKEW;
    // the A-to-B wire takes WIRE ps plus BUF ps of inserted delay.
    module shift2 #(parameter int SKEW = 50, WIRE = 10, BUF = 0) (
      input  logic clk, d_in,
      output logic q_b
    );
      timeunit 1ps; timeprecision 1ps;
      logic clk_b, q_a, d_b;
      assign #SKEW clk_b = clk;             // capture clock arrives late
      assign #(WIRE + BUF) d_b = q_a;       // the short path, plus any hold buffer
      dff_hold ff_a (.clk(clk),   .d(d_in), .q(q_a));
      dff_hold ff_b (.clk(clk_b), .d(d_b),  .q(q_b));
    endmodule
    

    Remember

    • Hold slack = t_cq,min + t_logic,min - (t_hold + skew). The clock period is not in it.

    • Positive skew (capture clock late) pushes the hold window later and eats hold slack; direct flop-to-flop links are the usual victims.

    • Recovery and removal are setup and hold for the release of an asynchronous reset; release it from a flop on the same clock.

    Rule of thumb: if a hold fix involves the clock period, it is not a hold fix.

    How this was checked

    Tool: Icarus Verilog 13.0, run on 2026-10-17. Commands: iverilog -g2012 -s tb_hold -o h hold.sv && vvp h (1000 ps period) and iverilog -g2012 -s tb_hold -P tb_hold.HALF=2000 -o h4 hold.sv && vvp h4 (2000 ps period), where hold.sv is the Testbench listing on the page.

    Result: both runs print 7 HOLD VIOLATION lines for the broken link, each 10 ps after the capture edge (edges at 550, 1550 ... ps and at 2050, 6050 ... ps), violations: bad=7 buffered=0 deskewed=0, the broken output unknown in 5 checked cycles, and PASS. The log on the page is copied from these runs, trimmed with ... lines.

    A specify block with $hold(posedge clk, d, 30) was also tried (iverilog -g2012 -gspecify): Icarus prints warning: Timing checks are not supported and ignores it, which is why the flop model writes the check by hand.

    Scope and limits: a behavioural model with fixed delays, one corner, no metastability; the X on a violation imitates a gate-level timing-check notifier. Real hold analysis is done by STA over every corner after clock-tree synthesis. The recovery and removal numbers are worked by hand, not simulated.

    Keep going

    • Practice: D flip-flop with asynchronous reset: glitch-free reset and metastability

    • Code lab: Build an 8-bit shift register

    • Interview: run a mock interview on this topic

    Related topics

    • Example 2: hold failure

    • Async assert, sync deassert

    Previous

    66 / 350

    Next: Counters and Timers

    Up next

    Up nextCounters and Timers

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Edge-Triggered State Elements
    • D Flip-Flop with Asynchronous Reset
    • Synchronous Reset and Register Enable