Does Clock Uncertainty Apply to Hold Checks?

Clock uncertainty setup vs hold explained: why the hold value is smaller, what jitter, skew and OCV each add, with SDC and real OpenSTA report lines.

By BitForBytes Editorial & Hardware Research Team, Official BitForBytes Hardware Publication · · 10 min read

⚡ Quick Summary

Clock uncertainty is a timing margin, set with set_clock_uncertainty, that covers clock jitter, skew that is not yet built, and extra guard band. It applies to both checks: the tool subtracts it from the required time for setup and adds it for hold. The hold value is smaller because hold compares one clock edge with itself, so period jitter cancels.


1. Introduction & Core Motivation

A real clock edge does not arrive exactly when the SDC file says it will. The PLL adds jitter, the clock tree delivers the edge to different flip-flops at slightly different times, and on-chip variation makes two identical buffers switch at different speeds. If static timing analysis assumed a perfect clock, a design could pass sign-off with zero slack and fail in silicon.

The first defence is a single number per clock: clock uncertainty. Nearly every SDC file you will ever open contains two lines, one with -setup and a larger value, one with -hold and a smaller value. Forum threads asking why the two differ go back years, and the replies are usually a single sentence. Interviewers know this, so they ask it: "Do we care about clock uncertainty for hold?", "Is OCV applied to setup, hold, or both?"

This article builds the answer from what each check compares, shows the exact report lines OpenSTA 3.1.0 prints when uncertainty is applied to a SkyWater SKY130 design, and separates uncertainty from on-chip variation (OCV), which students often merge. After reading, you will be able to budget both numbers, explain why they change after clock tree synthesis, and read them in a report.


2. Detailed Technical Breakdown

What Is Clock Uncertainty Made Of?

Clock uncertainty is the sum of three things the tool cannot yet compute exactly: jitter, unbuilt skew and a safety margin. Each belongs to a different stage of the flow.

ComponentPhysical causePresent before CTSPresent after CTS
JitterPLL and clock-source noise moving each edge in timeYesYes
Skew estimateClock tree not built yet, so arrival differences are unknownYesNo, real skew is computed from the propagated clock
MarginGuard band for effects not modelled elsewhereYesYes, often reduced

Before clock tree synthesis (CTS), clocks are ideal and reach every flop at the same instant, so the uncertainty has to stand in for the whole future tree. After CTS, set_propagated_clock makes the tool compute real clock arrival at each pin. The skew part must then be removed from the uncertainty, or it is counted twice.

Why Is Hold Uncertainty Smaller Than Setup Uncertainty?

Hold uncertainty is smaller because the hold check uses the same clock edge for launch and capture, while the setup check uses two different edges.

                edge N                          edge N+1
                  |                                |
 ideal CLK  ______/--------\______________________/-------
                 <->                              <->
              jitter band                      jitter band

 SETUP: launch at edge N, capture at edge N+1
        two different edges -> their jitter is independent
        worst case: edge N late, edge N+1 early
        => period jitter eats into the cycle

 HOLD : launch at edge N, capture at edge N
        the SAME source edge -> it cannot be early and late
        at once; period jitter cancels
        => only skew and variation between the two clock
           branches remain

In setup, the time available is the distance between edge N and edge N+1. If edge N leaves the PLL late and edge N+1 leaves early, the cycle shrinks by the period jitter. In hold, one source edge travels down two branches of the tree to the launch and capture flops. Whatever jitter that edge has, both flops see it. What can still differ is the delay of the two branches, which is skew and variation, not jitter.

How Do You Budget the Two Numbers?

You budget setup uncertainty as jitter plus skew estimate plus margin, and hold uncertainty as skew estimate plus margin, then drop the skew term after CTS. The values below are an illustrative budget, not numbers from a foundry.

TermValue
Clock jitter0.15 ns
Estimated skew before CTS0.10 ns
Margin0.05 ns
StageSetup uncertaintyHold uncertainty
Before CTS (ideal clock)0.15 + 0.10 + 0.05 = 0.30 ns0.10 + 0.05 = 0.15 ns
After CTS (propagated clock)0.15 + 0.05 = 0.20 ns0.05 ns

The uncertainty enters the two timing checks with opposite effect on the inequality but the same effect on slack: both get harder.

T_clk - T_unc,setup >= T_clk→q,max + T_comb,max + T_setup - T_skew
T_clk→q,min + T_comb,min >= T_hold + T_skew + T_unc,hold

where T_unc,setup and T_unc,hold are the setup and hold clock uncertainties, T_clk is the clock period, T_clk→q the launch flop's clock-to-Q delay, T_comb the logic delay, T_setup and T_hold the capture flop's requirements, and T_skew the capture-minus-launch clock arrival difference, all in ns.

Worked example. A path has T_clk = 3.2 ns, arrival T_clk→q + T_comb = 2.99 ns, T_setup = 0.11 ns and no skew. Without uncertainty, setup slack is 3.20 - 0.11 - 2.99 = 0.10 ns. With T_unc,setup = 0.30 ns, slack is 3.20 - 0.30 - 0.11 - 2.99 = -0.20 ns. A path that passed now fails, by exactly the difference between the uncertainty and the old slack.

What Does Clock Uncertainty Look Like in a Real Report?

In a report, clock uncertainty appears as its own line in the required-time section, negative for setup and positive for hold. The design is an 8-bit accumulator; the SDC lines are the ones you will see in production constraint files.

`default_nettype none
// acc8: 8-bit accumulator with a registered input.
// Reset: synchronous, active-high.
// Latency: din reaches sum after 2 clock edges.
module acc8 (
    input  wire       clk,
    input  wire       rst,
    input  wire [7:0] din,
    output wire [7:0] sum
);
    reg [7:0] din_q;
    reg [7:0] sum_q;

    always @(posedge clk) begin
        if (rst) begin
            din_q <= 8'h00;
            sum_q <= 8'h00;
        end else begin
            din_q <= din;             // launch register
            sum_q <= sum_q + din_q;   // adder, then capture register
        end
    end

    assign sum = sum_q;
endmodule
`default_nettype wire

Synthesised with Yosys 0.33 to the sky130_fd_sc_hd library, this gives 16 dfxtp_1 flip-flops and a ripple-carry adder; no latch is inferred because both registers are assigned in both branches.

# unc.tcl - effect of clock uncertainty on setup and hold
read_liberty sky130hd_tt.lib
read_verilog acc8_netlist.v
link_design acc8
create_clock -name clk -period 3.2 [get_ports clk]

set_clock_uncertainty -setup 0.30 [get_clocks clk]
set_clock_uncertainty -hold  0.15 [get_clocks clk]

report_checks -path_delay max
report_checks -path_delay min

The required-time section of the setup report:

   3.20    3.20   clock clk (rise edge)
   0.00    3.20   clock network delay (ideal)
  -0.30    2.90   clock uncertainty
   0.00    2.90   clock reconvergence pessimism
           2.90 ^ _89_/CLK (sky130_fd_sc_hd__dfxtp_1)
  -0.11    2.79   library setup time
           2.79   data required time
---------------------------------------------------------
           2.79   data required time
          -2.99   data arrival time
---------------------------------------------------------
          -0.20   slack (VIOLATED)

The required-time section of the hold report:

   0.00    0.00   clock clk (rise edge)
   0.00    0.00   clock network delay (ideal)
   0.15    0.15   clock uncertainty
   0.00    0.15   clock reconvergence pessimism
           0.15 ^ _82_/CLK (sky130_fd_sc_hd__dfxtp_1)
  -0.06    0.09   library hold time
           0.09   data required time
---------------------------------------------------------
           0.09   data required time
          -0.39   data arrival time
---------------------------------------------------------
           0.30   slack (MET)

Setup uncertainty moved the required time earlier, from 3.20 to 2.90 ns. Hold uncertainty moved the required time later, from 0.00 to 0.15 ns. Both cut slack by the uncertainty value.

RunSetup slackHold slack
No uncertainty0.10 ns0.45 ns
Setup 0.30 ns, hold 0.15 ns-0.20 ns0.30 ns
Same, plus 5% early/late derate-0.35 ns0.28 ns

How Is OCV Different From Clock Uncertainty?

On-chip variation is modelled by scaling cell and net delays, while clock uncertainty is a flat number applied at the clock edge. They cover different effects, and sign-off uses both.

PropertyClock uncertaintyOCV derate
SDC commandset_clock_uncertaintyset_timing_derate
What it changesRequired time, by a fixed amountEach delay, by a percentage
CoversJitter, unbuilt skew, marginProcess, voltage and temperature differences across one die
Applies to setupYesYes: late launch path, early capture clock
Applies to holdYesYes: early launch path, late capture clock
Changes after CTSSkew part removedStays, and now also scales the clock tree

The third row of the results table used set_timing_derate -late 1.05 and -early 0.95. Setup lost another 0.15 ns because the 2.99 ns data path was scaled up by 5%. Hold lost only 0.02 ns because its 0.39 ns path is short.

Derating creates one artefact. The part of the clock tree shared by the launch and capture flops gets counted as slow for one and fast for the other, which is physically impossible for the same buffers. Clock reconvergence pessimism removal (CRPR) gives that back. If the common clock path is 0.60 ns and the derates are 1.05 and 0.95, the credit is 0.60 × (1.05 - 0.95) = 0.06 ns. That is the "clock reconvergence pessimism" line in every report; it reads 0.00 above because the clock is ideal.


3. Practical Takeaways for Students

  • Exam angle: Define uncertainty as jitter plus skew estimate plus margin, write both inequalities with the uncertainty term, and state that setup uncertainty exceeds hold uncertainty because period jitter affects two-edge checks only. The pre-CTS versus post-CTS table is the most compact full-marks answer.
  • Interview angle: "Is uncertainty applied to hold?" Yes, added to the hold requirement. "Why is it smaller?" Same-edge check, jitter cancels. "Do you care about hold uncertainty in synthesis?" Little: clocks are ideal and hold is fixed after CTS, so synthesis targets setup. "Is OCV for setup only?" No, both, with opposite early/late assignment.
  • Common mistake: Leaving the pre-CTS uncertainty unchanged after CTS. The tool now computes real skew from the propagated clock, so keeping the skew estimate in the uncertainty double-counts it and makes the tool over-fix, wasting area and power.
  • Try it yourself: Run unc.tcl on your own netlist, then change the hold value to 0.50 and watch hold fail while setup is untouched. Verify the acc8 RTL in the BitForBytes Verilog Judge before synthesis.

4. Frequently Asked Questions

What is the difference between clock skew, jitter and uncertainty?

Skew is a fixed difference in clock arrival time between two flip-flops, caused by unequal clock-tree paths. Jitter is the variation of an edge's timing from cycle to cycle, caused by noise in the clock source. Uncertainty is the margin you give the timing tool to cover jitter, plus skew while it is still unknown.

Skew is spatial and can help or hurt a path depending on direction. Jitter is temporal and only ever hurts setup. After CTS the tool calculates skew itself, so uncertainty shrinks to jitter and margin.

Why does clock uncertainty change after clock tree synthesis?

Clock uncertainty changes because the largest unknown, skew, becomes known. Before CTS every flop sees an ideal clock, so designers put an estimated skew inside the uncertainty. After CTS the tool traces the real buffers and wires to each clock pin and applies the actual arrival times.

The skew estimate must be taken out at that point. A typical flow therefore has two sets of set_clock_uncertainty lines, one for pre-CTS runs and a smaller one for post-CTS runs.

Do FPGA timing tools apply hold uncertainty the same way?

Not always. Intel's knowledge base notes that the TimeQuest timing analyzer ignores a user set_clock_uncertainty -hold value when the hold check is on the same physical clock edge, and that Quartus II 11.1 SP1 and later added an -enable_same_physical_edge option to force it.

That behaviour follows the same reasoning as this article: for a same-edge check, source jitter cancels. FPGA tools also derive clock uncertainty automatically from the device's PLL and clock-network models.

Can a large clock uncertainty hide real timing problems?

A large uncertainty does not hide problems; it creates false ones. Every extra picosecond of margin is a picosecond the tool must recover by upsizing cells or inserting buffers, which costs area and power and can make timing closure impossible on paths that would have worked in silicon.

Too small a value is the dangerous direction, since paths pass sign-off with margin that jitter then consumes. The budget should come from the PLL specification and the clock-tree targets, not from habit.


5. References & Further Reading

  • J. Bhasker and R. Chadha, "Static Timing Analysis for Nanometer Designs: A Practical Approach", Springer, 2009 (clock uncertainty, on-chip variation, CRPR).
  • Synopsys Design Constraints (SDC) format: set_clock_uncertainty, set_timing_derate, set_propagated_clock.
  • OpenSTA, Parallax Software, github.com/parallaxsw/OpenSTA (command reference in doc/OpenSTA.pdf).
  • Intel FPGA Knowledge Base, article 000077546, "Why is my set_clock_uncertainty -hold constraint ignored by the TimeQuest timing analyzer?"

Frequently Asked Questions

What is the difference between clock skew, jitter and uncertainty?

Skew is a fixed difference in clock arrival time between two flip-flops, caused by unequal clock-tree paths. Jitter is the variation of an edge's timing from cycle to cycle, caused by noise in the clock source. Uncertainty is the margin you give the timing tool to cover jitter, plus skew while it is still unknown.

Why does clock uncertainty change after clock tree synthesis?

Clock uncertainty changes because the largest unknown, skew, becomes known. Before CTS every flop sees an ideal clock, so designers put an estimated skew inside the uncertainty. After CTS the tool traces the real buffers and wires to each clock pin and applies the actual arrival times.

Do FPGA timing tools apply hold uncertainty the same way?

Not always. Intel's knowledge base notes that the TimeQuest timing analyzer ignores a user set_clock_uncertainty -hold value when the hold check is on the same physical clock edge, and that Quartus II 11.1 SP1 and later added an -enable_same_physical_edge option to force it.

Can a large clock uncertainty hide real timing problems?

A large uncertainty does not hide problems; it creates false ones. Every extra picosecond of margin is a picosecond the tool must recover by upsizing cells or inserting buffers, which costs area and power and can make timing closure impossible on paths that would have worked in silicon.