Before CTS, hold checks are almost meaningless: with an ideal clock every flip-flop captures at exactly the same instant, and nearly every path is long enough. After CTS the clock tree has real latencies, small differences between branches become skew, and the shortest paths in the design can fail hold. This article shows hold checks after CTS on a 64-bit shift register, the worst case for hold, and how OpenROAD fixes hold violations with delay cells. It also explains useful skew, the deliberate use of clock skew to help timing.
8 min read | Intermediate
What You'll Learn
- Explain why hold is checked after CTS
- Read a hold path with the clock tree expanded
- Use clock uncertainty as hold margin
- Fix hold violations with
repair_timing -holdand measure the cost - Explain useful skew and its risks
The Hold Check After CTS
A hold check asks whether new data, launched by a clock edge, can reach the capture flip-flop before that flip-flop has finished capturing the old data at the same edge. The data must arrive no earlier than:
capture clock latency + hold time of the capture flip-flop (+ hold uncertainty)
The data arrives at launch clock latency + clock-to-Q + the shortest logic delay. Logic delay helps here; skew in the wrong direction hurts. The shortest possible path is a direct connection from one flip-flop to the next, as in a shift register:
module shift64 (
input clk,
input din,
output dout
);
reg [63:0] sr;
always @(posedge clk) begin
sr <= {sr[62:0], din};
end
assign dout = sr[63];
endmodule
A Hold Path With the Clock Expanded
After CTS in the lab's flow, with the clock propagated, the worst hold path of the shift register is:
0.00 0.00 ^ clk (in)
0.17 0.17 ^ clkbuf_0_clk/X (sky130_fd_sc_hd__clkbuf_4)
0.18 0.35 ^ clkbuf_3_6__f_clk/X (sky130_fd_sc_hd__clkbuf_4)
0.00 0.35 ^ _09_/CLK (sky130_fd_sc_hd__dfxtp_1)
0.31 0.66 ^ _09_/Q (sky130_fd_sc_hd__dfxtp_1)
0.00 0.66 ^ _10_/D (sky130_fd_sc_hd__dfxtp_1)
0.66 data arrival time
0.00 0.00 ^ clk (in)
0.17 0.17 ^ clkbuf_0_clk/X (sky130_fd_sc_hd__clkbuf_4)
0.19 0.36 ^ clkbuf_3_7__f_clk/X (sky130_fd_sc_hd__clkbuf_4)
0.00 0.36 ^ _10_/CLK (sky130_fd_sc_hd__dfxtp_1)
0.00 0.36 clock reconvergence pessimism
-0.02 0.34 library hold time
0.34 data required time
---------------------------------------------------------
0.32 slack (MET)
The two flip-flops are on different leaf buffers, and the capture side is 0.01 ns later: a small positive skew, against hold. Hold still passes by 0.32 ns, because the flip-flop's own clock-to-Q delay of 0.31 ns is larger than the skew plus the hold time. This is why a well-balanced tree with flip-flops that have a reasonable clock-to-Q rarely fails hold at a single corner.
Quick Check
Q: The hold time of the capture flip-flop in the path above is negative (-0.02 ns). What does a negative hold time mean?
Show Answer
The data may change slightly before the clock edge reaches the clock pin and the flip-flop still captures the old value correctly. The internal delay of the clock path inside the flip-flop is larger than that of the data path, so the requirement moves before the edge. It gives hold a little extra margin.
Hold Margin: Clock Uncertainty
Real designs must pass hold across process corners, voltage drops and variation in the clock tree, not just at the typical corner. A common way to keep margin while analysing one corner is hold uncertainty:
set_clock_uncertainty -hold 0.5 [all_clocks]
This subtracts 0.5 ns from every hold slack. On the shift register it turned a passing design into a failing one: without hold repair, the worst hold slack after routing is -0.18 ns. Here 63 of the 64 flip-flops fail. Only the first one does not, because its D input comes from the din port, which the constraints say arrives 2 ns after the clock edge, the lab's default input delay of 20 % of the period.
Fixing Hold: repair_timing -hold
Hold is fixed by making the short paths longer, which is the opposite of setup repair. OpenROAD inserts delay cells right in front of the failing D pins:
repair_timing -setup repair_timing -hold [INFO RSZ-0046] Found 63 endpoints with hold violations. [INFO RSZ-0032] Inserted 63 hold buffers.
The inserted cells are dlygate4sd3_1, a delay cell from the sky130 library designed to be slow while keeping a clean output edge. One went in front of each failing flip-flop, adding about 0.53 ns to its path. The result after routing:
| Shift register, 0.5 ns hold uncertainty | No hold repair | With repair_timing -hold |
|---|---|---|
| Worst hold slack | -0.18 ns | +0.36 ns |
| Endpoints failing hold | 63 | 0 |
| Worst setup slack at 10 ns | +7.32 ns | +7.31 ns |
| Cell area | 1,410 um2 | 2,041 um2 (+45 %) |
| Routed signal wire | 1.13 mm | 3.12 mm |
| Total power | 0.387 mW | 0.408 mW |
Hold is fixed with margin, at a high price for this design: 45 % more area, because a delay cell is about as large as the flip-flop it protects, and almost three times the wire, because every delay cell sits in the path between two flip-flops. The worst setup slack hardly moves, because the worst setup path runs to an output port, but every flip-flop to flip-flop path lost the 0.53 ns of its delay cell, which does not matter at 10 ns but would on a fast design. In a real block, only a small fraction of paths are this short, so the area cost is much smaller. It is still why hold is fixed with the least delay that is enough, and why hold fixing is done after the clock tree exists, never before.
Quick Check
Q: Why does the tool fix hold by adding delay to the data path rather than by changing the clock tree?
Show Answer
Changing the clock latency of a flip-flop moves both its capture and its launch timing, so it affects every path into and out of it, including setup paths that may be critical. A delay cell in front of one D pin affects only the paths into that pin, and only the short ones need it.
Useful Skew
Zero skew is the safe default, but not always the best. Suppose a path from FF1 to FF2 needs 6.5 ns at a 6 ns clock, while the path from FF2 to FF3 needs only 4 ns. Delaying FF2's clock by 0.6 ns gives the first path 0.6 ns more time and takes 0.6 ns from the second path, which has 2 ns to spare. Both paths now pass. That is useful skew: skew chosen on purpose to borrow time from paths with slack.
The costs follow from the setup and hold rules:
- Delaying FF2's clock makes hold harder on every path into FF2, by the same 0.6 ns.
- The extra latency is extra clock buffers and wire, which add power and are sensitive to variation.
- The paths out of FF2 lose time. If one of them is also critical, the borrowing does not work.
Commercial tools apply useful skew automatically during CTS and post-CTS optimization, guided by slack. The lab's flow builds for zero skew and does not apply useful skew; the multiplier's tree came out at 0.01 ns of skew.
Running It in ChipVerify Lab
Run Place and Route on a design with short flip-flop to flip-flop paths and read the final report's worst hold slack. The lab's flow always runs repair_timing -hold after setup repair, so with no uncertainty there is usually nothing to fix. With PRO, set a hold uncertainty of 0.5 ns in the panel, which adds set_clock_uncertainty -hold 0.5 [all_clocks] to the script, and compare the hold slack, area and number of dlygate cells with the run without it.
Common Mistakes
Mistake #1: Fixing hold before CTS
With an ideal clock there is no skew, so the hold picture is wrong. Delay cells added before CTS may be unnecessary afterwards, and they cost setup slack on every path they sit on.
Mistake #2: Checking hold only at the typical corner without margin
Hold depends on the fastest data path, which is fastest at the fast corner, while the clock skew can be worst elsewhere. Use the fast corner or a hold uncertainty as margin.
Mistake #3: Using useful skew without checking hold
Every picosecond of skew borrowed for setup is taken from the hold margin of the same flip-flop's input paths.
Practice Exercise
Challenge: How Much Delay Is Enough?
A hold path has a launch clock latency of 0.40 ns, clock-to-Q of 0.30 ns and no logic. The capture latency is 0.45 ns, the hold time 0.05 ns and the hold uncertainty 0.50 ns. What is the hold slack? How much delay must a delay cell add to give at least 0.10 ns of slack?
Hint
Arrival = launch latency + clock-to-Q + delay. Required = capture latency + hold time + uncertainty.
Solution
Arrival 0.70 ns, required 1.00 ns, slack -0.30 ns. To reach +0.10 ns the delay cell must add at least 0.40 ns.
Open the ready-made example in the lab, synthesize it and run Place and Route.
Summary
- Hold is checked meaningfully only after CTS, when real skew exists.
- A balanced tree and a flip-flop's clock-to-Q usually give hold margin: 0.33 ns on the shift register.
- With 0.5 ns of hold uncertainty, 63 paths failed;
repair_timing -hold, which the lab's flow runs after setup repair, fixed them with 63 delay cells. repair_timing -holdfixed them with 63 delay cells, at 43 % more area on this design.- Useful skew borrows time from paths with slack, at the cost of hold margin and clock power.