Skip to main content

repair-timing-grt

Investigate OpenROAD global-route repair_timing correctly — the grt "grind"/timeout, hold- and setup-buffer insertion. Covers why a skip-repair (fast) grt measures nothing about repair, why repair_timing must run inside global_route.tcl on the pre-GR ODB, how to split the grind into setup vs hold before blaming either, why the win is measured as clk_period-WNS in picoseconds rather than as a percentage of WNS, and why a design that closes measures nothing about a repair knob. Use when a grt stage times out or runs for hours, when a change is meant to affect repair_timing, or when reasoning about hold vs setup buffer insertion at global route.

Jump to install

Source facts

Repository
The-OpenROAD-Project/bazel-orfs
Last source activity
September 8, 2026 at 12:38
Detected SKILL.md language
English
Stars
26
Forks
23

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
repair-timing-grt
description
Investigate OpenROAD global-route repair_timing correctly — the grt "grind"/timeout, hold- and setup-buffer insertion. Covers why a skip-repair (fast) grt measures nothing about repair, why repair_timing must run inside global_route.tcl on the pre-GR ODB, how to split the grind into setup vs hold before blaming either, why the win is measured as clk_period-WNS in picoseconds rather than as a percentage of WNS, and why a design that closes measures nothing about a repair knob. Use when a grt stage times out or runs for hours, when a change is meant to affect repair_timing, or when reasoning about hold vs setup buffer insertion at global route.
## Goal Get a *trustworthy* answer about `repair_timing` at global route: whether it grinds, why, and whether a proposed change moves it. The traps below have each wasted hours-long runs; clear them all before drawing any conclusion. ## Trap 1 — a skip-repair / fast grt measures nothing about repair When the question is about **repair_timing** (the grt grind/timeout, hold- or setup-buffer insertion), do NOT use a fast/skip-repair grt flow (`SKIP_INCREMENTAL_REPAIR=1`, `SKIP_CTS_REPAIR_TIMING=1`, or a `grt_skiprepair`-style extract). It skips `repair_design` / `repair_timing` entirely — it only global-routes and estimates parasitics — so a clean, fast exit tells you **nothing** about whether repair grinds. Using it to "test the grind" is structurally meaningless. The fast-grt flow is built for clock **skew / parasitic re-measurement**, where skipping repair is exactly what you want. For anything about repair, run the FULL grt with repair enabled (`SKIP_INCREMENTAL_REPAIR=0`). ## Trap 2 — repair_timing needs the live global-route structures `repair_timing` must run **inside `global_route.tcl`, on the pre-GR ODB.** It needs the live global-route structures (GR grid, congestion, routing guides) that are **not** persisted in the saved post-GR ODB. So you cannot bolt `repair_timing` onto a saved post-GR ODB in a fresh session — it must happen in the same openroad session as `global_route`, which is exactly what `global_route.tcl` does: ```tcl # (shape of the flow script) global_route if { !$SKIP_INCREMENTAL_REPAIR } { repair_design_helper repair_timing_helper } ``` To test repair on a **modified netlist**, stage the PRE-GR ODB (the CTS-stage output) into the grt extract and run the grt stage with `SKIP_INCREMENTAL_REPAIR=0`, so `global_route` rebuilds the structures and repair runs live. Do NOT reuse a saved post-GR ODB. ## Trap 3 — measuring the win in WNS, or in percentages of it Report **`min_period = clk_period - WNS`**, and report differences between arms in picoseconds of that. Never a percentage of WNS. Near closure WNS is a small number, so a percentage of it is dominated by the clock you happened to pick rather than by anything the flow did: the same absolute improvement reads as 5% or 500% depending on the constraint. `min_period` is the quantity a designer actually trades against, it stays roughly invariant to the constraint (which is why it is comparable across arms at all), and a delta in it is a delta in what the design can run at. `check_pareto.py` already takes this position for its period axis — `clock - WNS`, never WNS itself, with the reasoning written out there — and `test/pre_route_pessimism/stage_ladder.tcl` reads it the same way. Two corollaries when the arms are repair settings: * **A percentage of a percentage is worse.** "The gap moved 17%" where the gap is itself a ratio of periods is unreadable. Quote the periods. * **Watch the constraint-invariance assumption.** It is only roughly true. Measured on one design, `min_period` at global route moved 322.3 -> 322.0 ps between a 1000 ps and a 330 ps clock (invariant to 0.1%), while at placement it moved 287.3 -> 285.7 ps between 330 and 290 (0.6%). Fine for comparing arms, not fine for treating a sub-percent difference as signal. ## Trap 4 — repair stops at WNS zero, so a closing design measures nothing `repair_timing` works until the violation is gone. Once WNS reaches zero it stops, so **every setting of a repair knob reaches the same floor** and the achieved period cannot separate them. What differs is runtime. This is not the same as "nothing to repair", and the difference matters when calibrating a study: * A design that closes *without* repair exercises no repair at all. Any knob measured on it reports zero because nothing ran. * A design that closes *because* repair worked exercised it fully. A knob swept there still cannot move the period. Both look identical in a post-repair slack report, which is the trap: reading positive WNS and concluding "the knob does nothing" conflates them. **And repair's recovery is not a fixed budget you can calibrate past.** It tracks the constraint. Measured on one design at global route, over two clocks 25 ps apart: | clock | min_period before repair | after repair | recovered | | --- | --- | --- | --- | | 290 ps | 322 ps | 289.7 ps (WNS +0.29) | 32 ps | | 265 ps | 322 ps | 265.8 ps (WNS -0.85) | 56 ps | Repair stopped just shy of the target in both cases, so post-repair `min_period` mostly echoes the clock rather than reporting a property of the design. Tightening the clock does not "get past" repair; it just makes repair work harder for the same near-zero result. So **calibrate to slightly negative WNS so repair is still working when it stops** — that is achievable and is what you want. But do not expect the achieved period to separate repair settings at such a clock: it cannot, by construction. Expect the separation in **runtime**, which is what a coverage knob like `TNS_END_PERCENT` claims to trade anyway, and report both. Achieved period only becomes informative at a clock repair demonstrably falls short of, which may be far tighter than the point WNS first goes negative. And turn off what you are not studying. Last gasp is the long tail of `repair_timing` and there is no reason to expect a `min_period` cliff in it, so `SKIP_LAST_GASP=1` buys back the runtime that makes an ensemble affordable. ## Trap 5 — split the grind into setup vs hold before blaming either The grind can be **setup-dominated** (a large count of setup-violated endpoints at negative clock slack — a design far from setup closure) or **hold-dominated** (many hold endpoints → many hold buffers), and the two want completely different fixes. A hold-side fix does not touch a setup-dominated grind, and vice versa. Before trusting any grt result about repair, always: 1. **Confirm `repair_timing` actually ran** — grep the log for `repair_timing`, `RSZ-` messages, and `Took N seconds: repair_timing`. If absent, a `SKIP_*` flag routed around it (Trap 1). 2. **Split setup vs hold** — read the setup-violated vs hold-violated endpoint counts and the setup- vs hold-buffer counts from the RSZ messages. Attribute the runtime to whichever dominates. 3. **Reconcile your hypothesis against the split.** If you framed the problem as a "hold pathology" but the measured grind is setup, the framing is wrong — fix the attribution before proposing a hold fix. ## Note on the effort dial `repair_timing -effort low|medium|high` instruments the **setup** phase's marginal-progress stop (it bails out of a non-convergent setup grind early). It does not currently have an equivalent knob for the hold phase, and hold repair is generally harder to bound than setup — so do not expect `-effort low` to shorten a hold-dominated grind. See the deps-preflight skill for confirming which effort actually reaches the tool. Pairs with `byo-openroad` (iterate on a repair change) and `orfs-deps-preflight` (validate the code path before the hours-long run).
View on GitHub