Fixing setup in layout means making the slow paths faster after the netlist has been placed: larger cells, buffers on heavy nets, swapped pins, and sometimes a different structure for the logic itself. OpenROAD does most of this automatically with repair_timing, but it can only go so far. This article follows the repair on the 16x16 multiplier at a 6 ns clock, shows what each kind of move does, where repair stops helping, and what an architecture change achieves that no layout fix can.
8 min read | Intermediate
What You'll Learn
- Describe the moves
repair_timinguses to fix setup - Read the repair table and judge how much a repair achieved
- Explain why repair on an estimate can miss in the extracted result
- Recognize when a path needs a different architecture, not more repair
The Repair Moves
repair_timing -setup works through the violating endpoints, worst first, and tries a sequence of moves on the cells of each path. The log lists the sequence:
[INFO RSZ-0100] Repair move sequence: UnbufferMove SizeUpMove SwapPinsMove BufferMove CloneMove SplitLoadMove
- Unbuffer: remove a buffer that only adds delay on a critical path.
- Size up: replace a cell with a stronger version of the same function, such as
maj3_1withmaj3_2. The stronger cell drives its load faster but loads its own driver more. - Swap pins: many cells have inputs with different delays. Moving the late-arriving signal to the fastest input of the same cell saves time at no cost in area.
- Buffer: insert a buffer to drive part of a heavy net, so the critical load sees a fast edge.
- Clone: duplicate a gate so that the critical loads get their own copy.
- Split load: move non-critical loads behind a buffer, leaving the critical one on the original net.
Many commercial flows also swap cells to a lower threshold voltage (Vt), which makes them faster at the cost of leakage. The sky130 hd library has a single Vt, and the repair summary reports it with 0 VT.
Repair on the Multiplier
The lab's flow runs repair_timing -setup once, after clock tree synthesis, with the per-layer RC values of OpenROAD-flow-scripts. On the multiplier at 6 ns the repair table reads:
Iter | Removed | Resized | Inserted | Cloned | Pin | Area | WNS | StTNS | EnTNS | Viol | Worst
| Buffers | Gates | Buffers | Gates | Swaps | | | | | Endpts | St/EnPt
0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -3.856 | -85.9 | -36.6 | 18 | _2415_/D
final | 0 | 91 | 53 | 0 | 42 | +6.7% | -1.043 | -26.9 | -4.8 | 8 | _2415_/D
In 441 iterations, the repair upsized 91 cells, inserted 53 buffers (7 of them to split loads), and swapped pins on 42 cells, for 6.7 % more cell area. The worst slack improved from -3.86 to -1.04 ns, the endpoint TNS from -36.6 to -4.8 ns, and the violating endpoints fell from 18 to 8. Then it stopped:
[WARNING RSZ-0062] Unable to repair all setup violations.
The table also shows why: the worst endpoint is the same flip-flop, _2415_/D, before and after. The repair made every cell on its path as fast as the library allows and still could not close it.
Quick Check
Q: Pin swapping changed 42 cells but costs no area. Why does it help timing at all?
Show Answer
Inputs of the same cell are not equally fast: the transistor stack makes some inputs reach the output sooner than others. If the latest-arriving signal sits on a slow input, swapping it with an early signal on a fast input shortens the critical path while the function stays the same, because the inputs are logically interchangeable.
What Is Left: Logic Depth
The remaining worst path after routing and extraction runs through 32 cells: a flip-flop, a buffer, then a chain of XORs, XNORs and majority gates (maj3, the carry function of an adder), ending in an OR and the capture flip-flop. Most stages are already size 2 or larger, the wire loads are 5 to 13 fF per stage, and the maj3_2 stages still take 0.20 to 0.35 ns each. The path is 7.27 ns of mostly cell delay against a 6 ns clock.
The timer agreed before any layout existed. Right after synthesis, with no wires at all, the worst slack was already -2.29 ns. Following the same design through the flow:
| Stage, 6 ns clock | Worst setup slack | Cell area |
|---|---|---|
| After synthesis, no wires | -2.29 ns | 9,668 um2 |
| After global placement, estimated wires | -3.72 ns | 10,067 um2 |
After CTS and repair_timing | -1.04 ns | 11,097 um2 |
| After global routing, estimated | -1.53 ns | 11,097 um2 |
| After detailed routing, extracted | -1.07 ns | 11,097 um2 |
Repair recovered all the delay the wires added and more, because it also upsized cells that synthesis had left at size 1. It could not remove the path's 32 stages.
Repairing Again After Global Routing
A common addition is a second repair after global routing, when the routes are known. OpenROAD supports it with incremental global routing, so that the moved cells are rerouted:
estimate_parasitics -global_routing global_route -start_incremental repair_timing -setup detailed_placement global_route -end_incremental
On the multiplier it inserted 6 more buffers and upsized 20 cells. The estimated worst slack improved from -1.53 to -1.43 ns, but the extracted slack after detailed routing ended at -1.11 ns, slightly worse than the -1.07 ns without it. A sizing-only variant ended at -1.08 ns. The second repair worked on the global-route estimate, which overstates short nets, as shown in Parasitics After Routing, so part of what it fixed was not a real problem. A repair step is only as useful as the parasitics it sees.
Changing the Architecture
The multiplier's critical path comes from its structure: Yosys builds the 16x16 multiplier as an array of adders, and the carries ripple through many of them. A Booth multiplier encodes the operand so that only about half as many partial products need to be added. Yosys can build one with its booth pass, run before techmap. The same RTL, the same 6 ns clock and the same layout flow:
| 6 ns clock | Array multiplier | Booth multiplier |
|---|---|---|
| Worst slack after synthesis, no wires | -2.29 ns | -1.92 ns |
| Cell area after synthesis | 9,668 um2 | 8,623 um2 |
| Worst slack after routing, extracted | -1.07 ns | -0.88 ns |
| Final cell area | 11,097 um2 | 10,246 um2 |
The Booth version is faster and 8 % smaller at the end of the flow. It still does not meet 6 ns. For that the design needs a pipeline register in the middle of the multiplier, which is an RTL change, or a longer clock period: at 10 ns the array multiplier closes with +0.12 ns of extracted slack.
Quick Check
Q: The slack after synthesis was -2.29 ns. Is that a reliable prediction of the final slack?
Show Answer
Not of the number, which ended at -1.07 ns after repair, routing and extraction. But it is a reliable warning: a design that misses timing by 38 % of the clock period before any wire exists has too much logic between its registers, and layout repair can only recover part of it.
Running It in ChipVerify Lab
Run Place and Route on the multiplier at a short clock period, such as 6 ns, and find the repair_timing table in the log: the iteration count, resized cells, inserted buffers and pin swaps, and the WNS before and after. Then run it at 10 ns and compare.
The lab's flow runs repair_timing once, after CTS, and its final timing report uses extracted parasitics; in the lab the multiplier at 6 ns ends at -1.06 ns, within 0.01 ns of the table above. With PRO, a custom script can add the repair after global routing. The Booth multiplier is a synthesis change: the Yosys booth pass.
Common Mistakes
Mistake #1: Expecting repair to fix any violation
Repair can make each cell on a path as fast as the library allows. If the path has too many stages, as the multiplier's 32-cell path does, the fix is in the RTL or the clock period.
Mistake #2: Judging a repair by the estimated slack
The second repair improved the estimate and made the extracted result slightly worse. Check every repair step against extracted parasitics.
Mistake #3: Ignoring the area cost
The first repair added 6.7 % area. Upsized cells and buffers also add power and can make placement and routing harder.
Practice Exercise
Challenge: How Much Did Repair Recover?
The worst path after extraction arrives at 7.27 ns, of which 0.33 ns is the clock delay to the launching flip-flop and the rest is the data path through 32 cells. If every cell on the data path could be made 10 % faster, would the design meet 6 ns? The clock also arrives 0.33 ns late at the capturing flip-flop, and its setup time is 0.13 ns.
Hint
Required time = clock period + capture clock delay - setup time. Only the data path part of the arrival time gets faster.
Solution
Required time = 6.00 + 0.33 - 0.13 = 6.20 ns. Data path = 7.27 - 0.33 = 6.94 ns; 10 % faster gives 6.25 ns, so the arrival is 0.33 + 6.25 = 6.58 ns, still 0.38 ns late. Even an optimistic across-the-board speed-up does not close the path; it needs fewer stages.
Open the ready-made example in the lab, synthesize it and run Place and Route.
Summary
repair_timingfixes setup with unbuffering, upsizing, pin swaps, buffering, cloning and load splitting; sky130 hd has no Vt swap.- On the multiplier at 6 ns it improved WNS from -3.86 to -1.04 ns for 6.7 % more area, then stopped on a 32-stage path.
- A second repair after global routing improved the estimate but not the extracted result.
- A Booth multiplier gave 0.19 ns better extracted slack with 8 % less area; closing 6 ns needs pipelining or a longer clock.