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

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • Logic Synthesis Glossary
  • Logic Synthesis Cheatsheet
  • Synthesis Foundations14
  • Logic Optimisation and Mapping20
  • Constraints and Timing15
    • SDC for Synthesis
    • Delay Models and Slack
      • Arrival, Required Time and Slack
      • Setup, Hold and Clock-to-Q
      • Cell Delay Models
      • Wire Delay at Synthesis Time
      • A Static Timing Calculator
    • Timing-Driven Synthesis
  • Sequential, FSM and Memory19
  • Datapath Synthesis14
  • Area, Power and Test14
  • Verification, ECO and Handoff15
  • Flows and Debugging15
  • Interview and Judgment11
Loading lesson

←Delay Models and Slack
  1. Topics
  2. Logic Synthesis
  3. Constraints and Timing
  4. Delay Models and Slack
  5. Setup, Hold and Clock-to-Q
On this page

    Part 3 of 9, lesson 7 of 15Part 3 of 9/Constraints and Timing/Lesson 7 of 15Intermediate

    Setup, Hold and Clock-to-Q

    Setup and hold checks on the shared path, with numbers from the DFF tables.

    Setup and hold look like two names for one margin until a hold violation survives every clock slowdown. The two checks differ in exactly one edge, and this page records that difference.

    After this lesson you can: run the setup check against the next edge and the hold check against the same edge, read both bounds from the DFF tables, and derive both slacks in a checked program.

    The question

    Every flop-to-flop path faces two checks. Setup asks whether the slowest data arrives before the next edge minus setup; hold asks whether the fastest data stays past the same edge plus hold. This lesson runs both on the shared vipexp path with numbers from the course.lib DFF tables, deriving both slacks in a checked program.

    The setup and hold bounds, and the clock-to-Q that feeds both arrivals, all come from the DFF cell in course.lib: a setup and a hold table on the D pin plus a clock-to-Q table on Q. All three share the teaching template (first index related_pin_transition, the data slew, second index constrained_pin_transition, the clock slew), read at the slews the calculator lesson propagates, so every number below traces to a library row.

    yosys
    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
    read_verilog <<EOV
    module vipexp(input clk, input a, b, c, d, input din, output reg qout, output t1, t2, t3, t4);
      reg q1;
      wire s1, s2, s3, s4;
      always @(posedge clk) q1 <= din;
      assign s1 = ~(q1 & a);
      assign s2 = ~(s1 | b);
      assign s3 = s2 ^ c;
      assign s4 = ~(s3 ^ d);
      always @(posedge clk) qout <= s4;
      assign t1 = s1;
      assign t2 = s2;
      assign t3 = s3;
      assign t4 = s4;
    endmodule
    EOV
    hierarchy -check -top vipexp
    synth -top vipexp -flatten
    dfflegalize -cell $_DFF_P_ 01 -cell $_DFF_PN0_ 01
    design -save generic
    dfflibmap -liberty course.lib
    tee -o abc_typ.txt abc -liberty course.lib -constr typical.constr -D 1000
    clean
    tee -o stat_typ.txt stat -liberty course.lib
    write_json net_typ.json
    select -assert-none t:$not t:$and t:$or t:$xor t:$mux t:$_NOT_ t:$_AND_ t:$_OR_ t:$_XOR_ t:$_NAND_ t:$_NOR_ t:$_DFF_P_ t:$_MUX_
    select -assert-count 2 t:DFF
    select -assert-count 1 t:NAND2
    select -assert-count 1 t:NOR2
    select -assert-count 1 t:XOR2
    select -assert-count 1 t:XNOR2
    check
    design -load generic
    dfflibmap -liberty course.lib
    tee -o abc_heavy.txt abc -liberty course.lib -constr heavy.constr -D 1000
    clean
    tee -o stat_heavy.txt stat -liberty course.lib
    select -assert-count 2 t:DFF
    select -assert-min 4 t:BUF
    check
    log PASS: setup and hold path mapped under two output loads

    The script maps vipexp twice, once under each load file. The assertions pin the typical cover as the last lesson did, and demand at least four buffers in the heavy cover, where ABC inserts one BUF between every stage and the next to carry the bigger tap loads. The typical stat histogram holds 6 cells including 2 flops, and the two ABC delays below are the whole experiment.

    Predict first The heavy load quadruples what each tap output must drive. Which check gets harder, and does the hold requirement move?

    Setup gets harder by about the arrival growth; the hold requirement does not move. The typical-load arrival is the model total from the last lesson, the heavy run adds delay on top, and the setup slack falls by that addition. Hold compares two arrivals from the same edge, so the period never enters it.

    Launch and capture edges

    Each register-to-register path starts at a launch edge and ends at a capture edge. The launch flop adds its clock-to-Q first, so both the slowest and the fastest arrival carry that term. On the shared path the model clock-to-Q is 0.163 ns, from the DFF row at the assumed 0.05 ns clock slew and the 0.01 pF pin load.

    Setup is checked against the next capture edge, one period later; hold against the same edge, zero cycles later. That single difference explains every fix: anything that moves the next edge touches setup only, because the hold comparison never sees a second edge.

    Check

    Capture edge

    Depends on the period

    Typical fix

    Setup

    Next edge, one period later

    Yes: a slower clock raises the requirement.

    Shorten the path or slow the clock.

    Hold

    Same edge, zero cycles later

    No: both sides share the edge, so the period cancels out.

    Add delay on the fast path; never slow the clock.

    Setup and hold checks

    The setup check compares the slowest arrival against the period minus setup time. With the hand-derived 1.00 ns period and 0.058 ns of model setup, the requirement is 0.942 ns against a model arrival of 0.781 ns, for a setup slack of plus 0.16 ns. The program below asserts that arithmetic, plus the table-point and monotonicity checks behind the interpolation.

    The hold check compares the fastest arrival against the hold time on the same edge. This library's rise and fall tables are identical, so the fastest arrival equals the slowest one; another library would need separate min-delay tables. Against 0.008 ns of model hold the slack is plus 0.77 ns: four stages of logic cannot race a same-edge check.

    example.py
    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
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    import sys
    
    # NLDM rows copied exactly from scripts/tools/synth-libs/course.lib, DFF cell.
    # Setup (D pin, rise_constraint) and hold share the teaching template: the first
    # index is the input slew, the second the clock slew; both span 0.01 to 0.1.
    SETUP_ROWS = 'values("0.05,0.06","0.06,0.07")'
    HOLD_ROWS = 'values("0.0,0.01","0.01,0.02")'
    CLOCK_SLEW = 0.05
    PERIOD = 1.0
    # Model arrival from the timing-calculator lesson (same path, same operating
    # point): rise and fall tables coincide in this library, so min equals max.
    ARRIVAL_MAX = 0.781
    ARRIVAL_MIN = 0.781
    
    def parse(rows):
        inner = rows[len('values("'):-len('")')]
        return [[float(x) for x in pair.split(",")] for pair in inner.split('","')]
    
    def bilin(table, slew, load):
        (v00, v01), (v10, v11) = table
        fs = (slew - 0.01) / 0.09
        fl = (load - 0.01) / 0.09
        return v00 * (1 - fs) * (1 - fl) + v01 * (1 - fs) * fl + v10 * fs * (1 - fl) + v11 * fs * fl
    
    def close(got, want, tol=0.001):
        return abs(got - want) <= tol
    
    SETUP = parse(SETUP_ROWS)
    HOLD = parse(HOLD_ROWS)
    DATA_SLEW = 0.042
    
    setup = bilin(SETUP, DATA_SLEW, CLOCK_SLEW)
    hold = bilin(HOLD, DATA_SLEW, CLOCK_SLEW)
    setup_slack = (PERIOD - setup) - ARRIVAL_MAX
    hold_slack_1ns = ARRIVAL_MIN - hold
    hold_slack_2ns = ARRIVAL_MIN - hold  # same edge: recomputed at a 2 ns period
    
    bad = 0
    def check(name, cond):
        global bad
        if not cond:
            print("FAIL: " + name)
            bad += 1
    
    check("setup at table point", close(bilin(SETUP, 0.01, 0.01), 0.05, 1e-9))
    check("hold at table point", close(bilin(HOLD, 0.01, 0.01), 0.0, 1e-9))
    check("setup rises with slew", bilin(SETUP, 0.1, 0.01) > bilin(SETUP, 0.01, 0.01))
    check("hold rises with slew", bilin(HOLD, 0.1, 0.01) > bilin(HOLD, 0.01, 0.01))
    check("setup time", close(setup, 0.058))
    check("hold time", close(hold, 0.008))
    check("setup slack", close(setup_slack, 0.161))
    check("hold slack", close(hold_slack_1ns, 0.773))
    check("hold ignores period", close(hold_slack_1ns, hold_slack_2ns, 1e-9))
    if bad != 0:
        sys.exit(1)
    print("PASS: setup plus 0.16 ns and hold plus 0.77 ns derived from DFF tables")

    Break it: quadruple the load

    The second run is the deliberate change: same RTL and target, but heavy.constr with its 0.2 pF load on every output. ABC answers with one BUF between every stage and the next, each driving its tap port and the following stage, so the recorded arrival grows from 610 ps to 1230 ps while the cell count rises from 4 to 8 and the area from 14 to 22. The checks did not move: setup still uses the 1.00 ns period minus setup, and hold still uses the same edge.

    Recomputed against the new arrival, setup slack falls by about the recorded growth: the comparison mixes a model typical arrival with a recorded heavy one (0.612 ns of fall against 0.62 ns of growth), and the setup bound shifts slightly with slew, so read it as about. The model arrival becomes about 1.39 ns (model clock-to-Q plus the recorded heavy combinational delay), so the setup slack is roughly 0.942 minus 1.39, a negative number: the same clock that met timing on the typical load now fails it. ABC's delay is the maximum over all its outputs, and the recorded netlist on the next page shows the worst path ends on the net the t4 port shares with the capture flop's D input, so that recorded delay is the capture path's and the failure claim belongs to it. Hold slack grows instead, since a later arrival sits further past the same-edge bound. One remap, two opposite verdicts, neither requirement moved.

    A sample report, labelled

    Commercial signoff reports show the same two checks with foundry numbers. The sketch below is hand-written to that shape, and every figure in it is invented: vocabulary transfer, not evidence.

    Diagram
    sample, hand-written report sketch (not a recording, not course numbers)
    setup: arrival 1.842 ns, required 2.000 - 0.120 = 1.880 ns, slack +0.038 ns MET
    hold:  earliest arrival 0.214 ns, required 0.050 ns, slack +0.164 ns MET
    critical endpoint: MEM_C/D, worst setup slack +0.038 ns

    Common mistakes

    Slowing the clock to fix a hold violation fixes nothing. Hold compares the earliest arrival with the same capture edge, so stretching the period changes neither side: the requirement has no period term.

    The recorded typical-load netlist with both DFF cells highlighted: the launch flop on the left edge and the capture flop on the right edge, with four combinational stages between them.

    Why does slowing the clock fix setup but never hold?

    Weak: setup needs more time and hold is about going too fast. Strong: setup compares the 0.781 ns model arrival against the next edge at 0.942 ns, so doubling the period to 2.00 ns lifts the slack to plus 1.16 ns; hold compares that arrival against a 0.008 ns bound on the same edge, so the slack stays plus 0.77 ns at any clock speed. Follow-up: the hold slack is negative. What do you change?

    Answer: add delay to the fast path, usually with buffers, and recheck setup too, since the added cells slow every path they touch.

    Check yourself The period grows from 1.00 ns to 2.00 ns with all delays fixed. Which slacks change, and by how much?

    Setup slack grows by exactly 1.00 ns, from plus 0.16 ns to plus 1.16 ns. Hold slack is unchanged at plus 0.77 ns: neither its arrival nor its bound mentions the period.

    Check yourself Under the heavy load the recorded delay roughly doubles. Which slack changes sign, and what does that prove about the checks?

    Setup slack goes negative because the arrival outgrows the fixed requirement: the clock that met timing now fails it, while hold slack only grows. That split proves the requirements belong to the clock and the edges, not to the load.

    • Setup uses the next capture edge, so the period sets its requirement; hold uses the same edge, so the period cancels out.

    • Clock-to-Q feeds both arrivals; setup and hold bounds come from the DFF tables at the data slew.

    • Slowing the clock never fixes a hold violation; only added delay on the fast path does.

    Rule of thumb: before touching the clock for a violation, ask which edge the check uses.

    How this was checked

    Arrival shifts and cell counts on this page come from one Yosys run of the script shown, recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): the vipexp block mapped with ABC against course.lib at a 1000 ps target with typical.constr and again with heavy.constr (0.2 pF load). The library is fictional, so absolute delays are teaching values. Setup and hold bounds and the slacks are model numbers interpolated from the exact DFF table rows, labelled so on the page; the hand-written commercial sketch is explicitly not a recording.

    • Interview: run a mock interview on this topic

    Related topics

    • Arrival, Required Time and Slack

    • A Static Timing Calculator

    • Setup, Hold and Slack

    Previous

    43 / 139

    Next: Cell Delay Models

    Up next

    Up nextCell Delay Models

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Wire Delay at Synthesis Time
    • A Static Timing Calculator