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
      • Clocks and Generated Clocks
      • Uncertainty, Latency and IO Delays
      • Driving Cells, Loads and Units
      • False, Multicycle and Max-Delay Paths
      • Missing and Over-Tight Constraints
    • Delay Models and Slack
    • 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

←SDC for Synthesis
  1. Topics
  2. Logic Synthesis
  3. Constraints and Timing
  4. SDC for Synthesis
  5. Uncertainty, Latency and IO Delays
On this page

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

    Uncertainty, Latency and IO Delays

    Clock uncertainty, latency, IO delays, and the available time they leave.

    The period is never the budget: the outside world, skew margin, and the flops themselves all take a share first. Spending the same period three ways, on paper and in bars, is what turns IO delays from file decoration into arithmetic.

    After this lesson you can: split a period into input, register, and output budgets, explain uncertainty against latency, and say why the output path subtracts no capture setup.

    The question

    A clock period is not a budget: a 10.0 ns period does not give the logic all of it. The outside world consumes a share before the signal reaches the input port and demands a share after it leaves the output port, and the clock itself arrives late and smeared. What is left after every claimant takes its share is the available time, and the four path classes split the period differently: input to register, register to register, register to output, and input to output straight through.

    This lesson builds those four splits from one EXAMPLE period and hand-derived arithmetic, every figure and table labelled hand-derived. The recorded run proves the registered classes exist in real structure, two flops of it, before any budget is spent.

    One chain, one run

    The design below is a two-flop chain: req captured twice, then driven out as ack. That gives the three registered paths in the smallest possible structure. The script synthesises the chain and asserts the two flops; the second half is the break-it preview, req wired straight to ack with no flop at all.

    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
    read_verilog <<EOV
    module io_chain(input clk, input req, output ack);
    reg a, b;
    always @(posedge clk) begin a <= req; b <= a; end
    assign ack = b;
    endmodule
    EOV
    synth -top io_chain
    tee -o stat_chain.txt stat
    write_json net_chain.json
    select -assert-count 2 t:$_DFF_P_
    check
    design -reset
    read_verilog <<EOV
    module io_direct(input clk, input req, output ack);
    assign ack = req;
    endmodule
    EOV
    synth -top io_direct
    tee -o stat_direct.txt stat
    write_json net_direct.json
    select -assert-none t:$_DFF_P_
    check
    log PASS: two flops make the chain and a wire needs none

    Port directions stay fixed for the rest of the topic: req is an input, ack is an output, so every input delay belongs to req and every output delay to ack.

    What the run recorded

    Predict first The chain clocks req through two flops; the direct version wires req to ack. Which paths can each hold?

    The chain has the three registered classes: 2 cells, 2 flops. The direct version has the fourth class only (no cells), one feedthrough path the port delays bound to 4.0 ns.

    Version (recorded)

    Cells (recorded)

    Flops (recorded)

    Chain

    2

    2 flops

    Direct wire

    no cells

    no flops

    The chain netlist holds 2 cells, both flops: req in, two flops, ack out, each gap one registered path. The direct version is the fourth class: a feedthrough wire budgeted only from the port delays.

    Recorded chain: 2 cells, req through two flops to ack.

    The SDC that bounds the chain

    The constraint below bounds the same chain the run just proved: EXAMPLE period, uncertainty margin on every path, source latency shifting arrival, input delay before req, and output delay after ack, external setup included. The numbers live in the listing and the table; the prose names only who spends what.

    Diagram
    # not-run: no SDC reader in the open tools
    create_clock -name CLK -period 10.0 [get_ports clk]
    set_clock_uncertainty -setup 0.5 [get_clocks CLK]
    set_clock_latency -source 1.2 [get_clocks CLK]
    set_input_delay -clock CLK 3.0 [get_ports req]
    set_output_delay -clock CLK 2.5 [get_ports ack]

    Uncertainty and latency are routinely confused. Uncertainty is margin the tool must not spend. Latency shifts both edges together, so the period is unchanged. One shrinks the budget; the other moves the window.

    Uncertainty against latency

    Uncertainty exists because synthesis runs on the previous lesson's ideal clock: zero skew by assumption. Silicon has skew, so the constraint holds back 0.5 ns on every setup check, plus jitter. That margin comes off the usable period on all four path classes, which is why the budgets below subtract it first.

    Latency is the delay from the clock source to the flop clock pin: source latency before the definition point, network latency after it. It does not shrink the period: if launch and capture both shift by the same 1.2 ns, the distance between them is still the full period. It changes absolute arrival against the outside world, which the input and output delays reconcile.

    The four budgets

    The program below spends the period four times in integer picoseconds, with an EXAMPLE clock-to-Q of 0.8 ns and an EXAMPLE setup of 0.5 ns. The input path leaves a 6.5 ns remainder and a 6.0 ns budget. The register path is period minus clock-to-Q, uncertainty, and setup: 8.2 ns. The output path is period minus clock-to-Q and the output delay: 6.2 ns, no capture setup subtracted. The feedthrough path is period minus both external delays and uncertainty: 10 minus 3 minus 2.5 minus 0.5, a 4.0 ns budget with no flop at either end. Ten self-checks pin every remainder, every budget, and every sum back to the period.

    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
    
    # IO budget arithmetic in integer picoseconds. EXAMPLE parameters, hand-derived.
    PERIOD_PS = 10000
    IN_DELAY_PS = 3000
    OUT_DELAY_PS = 2500
    UNC_PS = 500
    SETUP_PS = 500
    TCQ_PS = 800
    
    # Input path: the outside world owns the input delay; uncertainty and the
    # capturing flop's setup come off the remainder.
    in_avail = PERIOD_PS - IN_DELAY_PS - UNC_PS
    in_comb = in_avail - SETUP_PS
    # Register-to-register path: launch clock-to-Q, uncertainty, capture setup.
    reg_comb = PERIOD_PS - TCQ_PS - UNC_PS - SETUP_PS
    # Output path: launch clock-to-Q plus the output delay, which already holds
    # the external setup; no capture setup is subtracted here.
    out_avail = PERIOD_PS - OUT_DELAY_PS - UNC_PS
    out_comb = out_avail - TCQ_PS
    # Input-to-output feedthrough: no flop at either end, so both external
    # delays and the uncertainty come off the period directly.
    inout_comb = PERIOD_PS - IN_DELAY_PS - OUT_DELAY_PS - UNC_PS
    
    if in_avail != 6500:
        print("FAIL: input remainder got " + str(in_avail) + " want 6500")
        sys.exit(1)
    if in_comb != 6000:
        print("FAIL: input budget got " + str(in_comb) + " want 6000")
        sys.exit(1)
    if reg_comb != 8200:
        print("FAIL: register budget got " + str(reg_comb) + " want 8200")
        sys.exit(1)
    if out_avail != 7000:
        print("FAIL: output remainder got " + str(out_avail) + " want 7000")
        sys.exit(1)
    if out_comb != 6200:
        print("FAIL: output budget got " + str(out_comb) + " want 6200")
        sys.exit(1)
    if inout_comb != 4000:
        print("FAIL: feedthrough budget got " + str(inout_comb) + " want 4000")
        sys.exit(1)
    if in_comb + IN_DELAY_PS + UNC_PS + SETUP_PS != PERIOD_PS:
        print("FAIL: input parts do not sum to period")
        sys.exit(1)
    if reg_comb + TCQ_PS + UNC_PS + SETUP_PS != PERIOD_PS:
        print("FAIL: register parts do not sum to period")
        sys.exit(1)
    if out_comb + TCQ_PS + OUT_DELAY_PS + UNC_PS != PERIOD_PS:
        print("FAIL: output parts do not sum to period")
        sys.exit(1)
    if inout_comb + IN_DELAY_PS + OUT_DELAY_PS + UNC_PS != PERIOD_PS:
        print("FAIL: feedthrough parts do not sum to period")
        sys.exit(1)
    
    print("PASS: comb budgets 6.0 ns in and 8.2 ns through and 6.2 ns out and 4.0 ns feedthrough of 10.0 ns")

    Path (hand-derived)

    Consumes (hand-derived)

    Combinational budget (hand-derived)

    Input to register

    Input delay 3.0 ns, uncertainty 0.5 ns, setup 0.5 ns

    6.0 ns

    Register to register

    Clock-to-Q 0.8 ns, uncertainty 0.5 ns, setup 0.5 ns

    8.2 ns

    Register to output

    Clock-to-Q 0.8 ns, output delay 2.5 ns, uncertainty 0.5 ns

    6.2 ns

    Input to output

    Input delay 3.0 ns, output delay 2.5 ns, uncertainty 0.5 ns

    4.0 ns

    Each row's consumers plus its budget sum back to the period; the program proves all four sums. The output row is the one people get wrong: its capture flop sits outside the block, so its setup is part of the 2.5 ns output delay, never a separate subtraction. The feedthrough row has no clock-to-Q and no setup at all.

    Input path: delay, uncertainty, budget and setup fill the period.
    Register path: clock-to-Q, budget, setup and uncertainty fill the period.
    Output path: clock-to-Q, budget, output delay and uncertainty fill the period.
    Feedthrough path: input delay, budget, output delay and uncertainty fill the period.

    Break it: remove the registers

    The second run in the script is the break-it: req tied straight to ack, no flop left in the path. The flop count falls from 2 flops to no flops, and the three registered paths collapse into the fourth class: registers removed, so one feedthrough path with a 4.0 ns budget, bounded only by the input delay, the output delay, and uncertainty.

    Two opposite slips empty the budget. A zero input delay reserves nothing for the outside world, so the available time is overstated by the full external delay. And a capture setup subtracted on the output path double-counts the external setup already inside the output delay, understating the budget.

    Derive the available time, and say what uncertainty covers.

    • Weak answer: subtract every number from the period on every path.

    • Strong answer: each path starts from the period minus its own external share: the input path keeps 6.0 ns from a 6.5 ns remainder, the register path keeps 8.2 ns, the output path keeps 6.2 ns with no capture setup subtracted, and the feedthrough path keeps 4.0 ns from both external delays. Uncertainty is the skew and jitter margin held back on all four; latency shifts arrival without shrinking the period.

    • Follow-up: the output delay grows by 1 ns. Which budgets move? The output and feedthrough budgets, down by exactly 1 ns each; the input and register paths never see that port.

    Check yourself The period is 10.0 ns, the input delay is 3.0 ns, uncertainty is 0.5 ns, and EXAMPLE setup is 0.5 ns. What is the combinational budget?

    Remainder 6.5 ns minus setup: 6.0 ns, because the capturing flop sits inside the block.

    Check yourself With the same period and uncertainty, the output delay is 2.5 ns and EXAMPLE clock-to-Q is 0.8 ns. What is the output budget, and what is the feedthrough budget?

    Output: 10.0 minus 2.5 minus 0.5 minus 0.8, which is 6.2 ns, with no capture setup subtracted. Feedthrough: 10.0 minus 3.0 minus 2.5 minus 0.5, which is 4.0 ns.

    • Uncertainty is skew and jitter margin held back on every path; latency shifts arrival without shrinking the period.

    • The output delay already holds the external setup, so no capture setup is subtracted on the output path.

    • req is an input and ack is an output everywhere in this topic.

    Rule of thumb: spend the period per path, and never subtract a setup that lives outside the block.

    How this was checked

    Cell and flop counts and the netlist picture on this page come from one Yosys 0.70 run of the script shown, recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): a two-flop chain and a direct-wire variant are synthesised, with write_json netlists and flop-count assertions. All budgets, remainders, and timing bars come from the stdlib-only Python program shown, which asserts every figure it prints, and are labelled hand-derived. The SDC listing is illustrative: the open tools have no SDC reader.

    • Interview: run a mock interview on this topic

    Related topics

    • Driving Cells, Loads and Units

    • Delay Models and Slack

    • See also: Skew and Variation

    Previous

    38 / 139

    Next: Driving Cells, Loads and Units

    Up next

    Up nextDriving Cells, Loads and Units

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • False, Multicycle and Max-Delay Paths
    • Missing and Over-Tight Constraints