VLSI

Clock uncertainty in STA

By CourseTron Team ·  8 min read ·  Updated Oct 2026 💬 𝕏 in f
Table of Contents
    ? Print / Save as PDF

    In short: Clock uncertainty is the single number STA subtracts from (setup) or adds to (hold) the ideal clock edge to budget for jitter plus skew that the timing model cannot yet see — you set it generously before CTS with set_clock_uncertainty, then drop it to just jitter afterwards once real skew is in the clock tree.

    Every synchronous path in a chip is a race between data and clock. Static timing analysis (STA) decides who wins by comparing when data arrives against when the capture clock edge arrives. But the clock edge is never perfectly where the ideal model says it is. Clock uncertainty is the deliberate pessimism we inject to cover that wobble, and knowing how to size it — and when to shrink it — separates a timing engineer from a tool operator.

    What clock uncertainty actually represents

    Uncertainty bundles two physically different effects into one margin that the timing engine applies at the clock pin of every flop:

    • Jitter — cycle-to-cycle variation in the clock edge, caused mostly by the PLL and by power-supply noise coupling into the clock network. It is a temporal effect that exists even on a perfect, zero-skew tree.
    • Skew margin — a budget for the spatial difference in arrival time between the launch and capture clock pins. Before the clock tree is built, the tool has no idea what this skew will be, so you reserve for it up front.

    A third contributor often folded in is OCV / margin for modeling error — extra guard-band for on-chip variation, crosstalk on the clock, and library characterization inaccuracy. In older flows this all lived inside a single set_clock_uncertainty value; modern Synopsys PrimeTime and Cadence Tempus flows prefer to model OCV and crosstalk explicitly (AOCV/POCV, SI analysis) and keep uncertainty closer to pure jitter.

    Setup check with clock uncertainty Launch FF Combinational logic Capture FF clock network capture edge pulled earlier by uncertainty CourseTron · coursetron.com
    Setup check: required time at the capture flop is pulled earlier by the uncertainty window (dashed), tightening the budget.

    How the tool applies it to setup and hold

    For a single-cycle path at period T, the classic setup inequality the engine solves is:

    T_launch + T_cq + T_logic_max + T_setup ≤ T + T_capture − Uncertainty_setup

    Uncertainty is subtracted from the available period, so it directly eats into your setup slack. For hold, it works the other way — it is added to the required time, making the hold check harder:

    T_launch + T_cq + T_logic_min ≥ T_capture + T_hold + Uncertainty_hold

    Rule of thumb: setup uncertainty includes jitter + skew margin + setup OCV guard-band; hold uncertainty usually only needs jitter + hold skew margin, because the PLL period variation does not help a hold check. That is why set_clock_uncertainty -setup and -hold take different numbers.

    Writing the constraint correctly

    The SDC command looks trivial but the options matter. A typical pre-CTS block for a 1 GHz clock (T = 1000 ps) might read:

    • set_clock_uncertainty -setup 150 [get_clocks clk]
    • set_clock_uncertainty -hold 80 [get_clocks clk]

    Here 150 ps is roughly 15% of the period — generous, because no real tree exists yet. You can also apply inter-clock uncertainty between two different clocks: set_clock_uncertainty -from clk_a -to clk_b 200 covers the extra pessimism on asynchronous or ratio-synchronous crossings. Always confirm the units with set_units or the design's .lib; a stray factor of 1000 in picoseconds versus nanoseconds has sunk many a signoff.

    Pre-CTS vs post-CTS: the number changes

    This is the concept interviewers probe hardest. The value of uncertainty is not constant across the flow — it is a moving placeholder for how much of the clock timing is still unknown.

    StageClock modelWhat uncertainty must coverTypical value (1 GHz)
    Pre-CTS (synthesis / floorplan / place)Ideal clock, zero skew, estimated latencyPLL jitter + full skew budget + OCV guard~120–200 ps
    Post-CTS (propagated clock)Real tree, real skew & latency computedJitter only (+ small residual margin)~40–80 ps
    Signoff STAPropagated + SI + OCV/POCVJitter + any crosstalk/OCV not modeled explicitly~30–60 ps

    Pre-CTS, you set set_propagated_clock off and the clock is ideal, so skew is literally zero in the model. If you did not reserve skew margin inside uncertainty, synthesis would happily build logic that fails the instant a real tree introduces skew. After clock tree synthesis you run set_propagated_clock [all_clocks]; now the tool computes genuine insertion delay and skew per path, so the skew component of uncertainty has become real data — you remove it and leave only jitter. Forgetting to lower uncertainty post-CTS is a classic over-pessimism bug that burns real area and power chasing slack that does not exist.

    Jitter: the edge never lands twice in the same place dashed = ideal edge ± uncertainty band solid = actual edge CourseTron · coursetron.com
    The orange edge wanders within the uncertainty band every cycle — that wander is what jitter margin pays for.

    A worked number

    Say your path has T_cq = 90 ps, T_logic_max = 760 ps, T_setup = 45 ps at T = 1000 ps, with equal launch/capture latency. Pre-CTS with 150 ps uncertainty: required = 1000 − 150 = 850 ps; arrival = 90 + 760 + 45 = 895 ps → slack = −45 ps (fails). You might wrongly over-optimize. Post-CTS the real skew turns out helpful and uncertainty drops to 60 ps: required = 940 ps, arrival 895 ps → slack = +45 ps. The path was fine all along; the pre-CTS failure was budgeted pessimism, exactly as intended.

    Same path, T = 1000 ps: arrival 895 ps Pre-CTS (uncertainty 150 ps) arrival 895 ps required 850 ps, slack -45 ps Post-CTS (uncertainty 60 ps) arrival 895 ps required 940 ps, slack +45 ps 0 1000 ps CourseTron · coursetron.com
    Worked example: identical 895 ps arrival, but lowering uncertainty from 150 ps to 60 ps moves required time from 850 ps to 940 ps.

    If your design is still failing setup after CTS with correct uncertainty, that is a genuine timing problem — resize, restructure logic, or pipeline. If it fails only pre-CTS, confirm your uncertainty is not double-counting skew the tree will later fix.

    Common mistakes engineers make

    • Using the same uncertainty for setup and hold — hold rarely needs the full period-jitter term.
    • Never lowering the number after CTS, leaving signoff permanently pessimistic.
    • Letting uncertainty and explicit OCV/SI double-count the same crosstalk, so margins stack.
    • Applying a single clock-level value when specific generated or divided clocks need their own, tighter budgets.
    • Ignoring inter-clock uncertainty on domain crossings, then debugging phantom failures in signoff.

    For a deeper treatment of the spatial half of this margin, read our companion piece on clock skew in VLSI, and pair it with setup and hold time violations to see how the budget is actually spent. If you are evaluating signoff flows for a real tapeout, the design-services team at Avecas VLSI design services runs these margins daily on production blocks.

    Want to go from "I can define uncertainty" to "I can defend my signoff margins in an interview"? CourseTron's physical-design and STA track walks through PrimeTime and Tempus constraint setup on real blocks. Explore the VLSI courses →

    FAQ

    Is clock uncertainty the same as clock skew?

    No. Skew is the actual spatial difference in clock arrival between two flops and becomes real data after CTS. Uncertainty is a margin you set manually; before CTS it reserves for unknown skew, after CTS it mostly represents jitter since real skew is now computed directly.

    Why is setup uncertainty usually larger than hold uncertainty?

    Setup must absorb PLL period jitter, skew margin, and OCV guard-band across a full cycle. Hold is a same-edge check within one clock period, so period jitter does not relax it — you only budget jitter and a small skew margin, giving a smaller number.

    What command sets it and how do I change it after CTS?

    Use set_clock_uncertainty -setup/-hold [get_clocks clk]. After clock tree synthesis, run set_propagated_clock [all_clocks] and reduce the uncertainty to the jitter-dominated value, since real skew and latency are now in the model.

    Does uncertainty affect both setup and hold slack?

    Yes, in opposite directions. It is subtracted from the available period for setup (reducing setup slack) and added to the required time for hold (reducing hold slack). That is why the tool lets you specify the two independently.

    Advertisement