← Home Timing & Power Optimization
4 Timing & Power Optimization

Post-CTS Optimization

Pre-CTS, post-CTS, post-route optimization, useful skew, SI fixes, leakage reduction and ECO closure

Before clock tree synthesis (CTS), the optimizer worked with an ideal clock: every register received its clock edge at the same instant, with zero insertion delay and zero skew. That assumption let the placement-stage engine focus almost entirely on data-path setup timing. The moment the clock tree is built and the clock becomes propagated, that comfortable fiction disappears. Now every clock pin has a real arrival time, the clock network has genuine latency, and registers see one another across actual skew. Post-CTS optimization is the stage where the timing engine confronts this reality — and it must do so without undoing the tree that CTS just spent enormous effort balancing. This chapter explains what genuinely changes once the clock is propagated, why hold violations suddenly dominate the conversation, how useful skew becomes a real lever, and how to drive the optimizer to fix violations without corrupting the clock network or over-correcting one constraint at the expense of another.

Technical diagram

From Ideal to Propagated: What Actually Changes

The single biggest mental shift is that skew is now real. Under an ideal clock, launch and capture registers were assumed to clock simultaneously, so skew contributed nothing to either setup or hold. Once propagated, skew can either help or hurt. Positive skew toward the capture register relaxes setup but tightens hold; positive skew toward the launch register does the reverse. The optimizer can no longer reason about a path purely from combinational delay — it must add the clock arrival difference into every setup and hold check. This has three immediate consequences:

  1. 1. Hold becomes a first-class concern. Pre-CTS, hold was often deferred because ideal-clock skew

was zero and hold slack was dominated by clock-to-q plus minimum data delay. With real skew, a short data path between two registers whose clock arrivals differ can now fail hold. Post-CTS is the first stage where hold violations appear in large numbers and must be addressed systematically.

  1. 2. Useful skew is available. Because clock arrival times are now concrete, the engine can

deliberately retain (or request) small skews that move slack from a path with margin to a path that needs it. This is a genuine optimization degree of freedom that simply did not exist when the clock was ideal.

  1. 3. Restructuring must be cautious near clock cells. The data-path netlist now shares the floorplan

with thousands of clock buffers and inverters that CTS placed with care. Aggressive cell moves, gate cloning, or large buffer insertions near the clock spine risk disturbing the balanced tree, creating congestion around clock cells, or perturbing the very latencies the timing report depends on.

AspectPre-CTS (ideal clock)Post-CTS (propagated clock)
Clock modelIdeal, zero skew, zero insertion delayPropagated, real skew and latency
Primary timing focusSetup on data pathsSetup and hold together
Hold violationsMostly hidden / deferredFirst-class, abundant, must fix
Useful skewNot availableAvailable as a real lever
Restructuring freedomBroad (placement still fluid)Constrained near clock cells
Clock cellsNot yet presentPresent, must be protected
Typical net movementLarge legal placement changesIncremental, localized changes
Risk if over-aggressiveWasted area/powerCorrupting the clock tree

Correcting Violations in Post-CTS Mode

The post-CTS optimizer runs against the propagated clock and tries to close whatever remains open. The work splits into three buckets: setup recovery, hold fixing, and design-rule (transition/capacitance) cleanup. Each has its own toolkit and its own cautions. Setup recovery. Setup paths that were closed under the ideal clock can re-open once real skew and insertion delay are folded in — particularly paths where the capture clock now arrives earlier than assumed. The classic remedies still apply: upsize critical-path gates to reduce stage delay, insert buffers to fix slow transitions that inflate delay, restructure logic to shorten the critical depth, and pull cells slightly to shorten routing on the worst nets. The difference from pre-CTS is restraint — large moves are discouraged, and the engine generally prefers in-place sizing (swapping a cell for a stronger or faster-Vt variant in the same footprint) over relocating logic. Hold fixing. This is the headline activity of post-CTS. A hold violation means data arrives at the capture register too soon relative to the clock edge, so the fix is to add delay to the short data path. The standard approaches, in rough order of preference:

  • Insert delay (buffer) cells on the violating data path. Dedicated delay cells are preferred because they add delay with minimal drive-strength side effects and predictable area.
  • Downsize a driver on the short path so it propagates more slowly, recovering hold without adding cells — useful when the same net has setup margin to spare.
  • Swap to a slower threshold-voltage variant (e.g., a higher-Vt cell) where the library offers it, trading a little speed for hold margin and often saving leakage power at the same time.
  • Exploit useful skew by allowing the capture clock to arrive slightly earlier (or the launch clock later) so the existing data delay satisfies hold — only viable if it does not break the setup of the same or neighboring paths. Transition and capacitance cleanup. Real clock and data routing now exists, so max-transition and max-capacitance design-rule violations are meaningful. These are typically resolved by buffering long

nets, upsizing weak drivers, or splitting high-fanout nets. Cleaning these up matters because a slow transition both degrades timing accuracy and increases short-circuit power.

ViolationTypical fixCaution
Setup (data tooUpsize gate, faster-Vt swap,Avoid large moves near clock cells; don't add
late)restructure, local bufferhold violations
Hold (data tooInsert delay cells, downsize driver,Don't over-insert and erode setup margin or
early)slower-Vt swaparea/power
Max transitionBuffer long net, upsize driverNew buffers can disturb local skew if placed

in clock region

Max capacitanceSplit high-fanout net, upsize driverWatch added load on clock-adjacent nets
Skew imbalanceApply useful skew, retune clock cellsTouching clock cells can re-balance the

whole tree

Technical diagram

Generic Command Sketches

The exact syntax varies by toolchain, but the conceptual flow is portable. First, the clock must be made propagated so the engine optimizes against real arrival times rather than the ideal model.

# Switch from ideal to propagated clock before post-CTS optimization
set_propagated_clock [all_clocks]                 # standard (SDC)
# Run the post-CTS optimization pass against the propagated clock
optimize_design -post_cts                         # illustrative — generic, not tool-
specific

Hold fixing is usually expressed as a goal with a target slack and a preferred fix strategy. Note the explicit instruction to leave the clock tree alone — this is the single most important guardrail at this stage.

# Fix hold while protecting the clock network and honoring don't-touch
fix_timing -hold \
-target_slack 0.020 \
-prefer delay_cells \
-dont_touch_clock_tree true                   # illustrative — generic, not tool-
specific
# Tell the engine not to disturb clock cells or protected regions
set_dont_touch [get_cells -hierarchical *clk_buf*] true   # illustrative — generic,
not tool-specific
set_dont_touch [get_cells my_hard_macro_wrapper] true     # illustrative — generic,
not tool-specific

Because the first pass rarely closes everything cleanly, post-CTS optimization is iterative. An incremental pass re-optimizes only what is still violating, leaving converged paths untouched so the design does not oscillate.

# Incremental post-CTS cleanup: setup then a guarded hold pass
optimize_design -post_cts -incremental -setup     # illustrative — generic, not tool-
specific
optimize_design -post_cts -incremental -hold \
-max_hold_buffers 50000                        # illustrative — generic, not
tool-specific
report_timing -delay_type min -path_type full      # standard (SDC-style reporting
intent)

Additional Levers, Incremental Passes, and Protecting the Clock

Beyond the basic fix menu, post-CTS exposes several finer controls. You can bias the optimizer toward area or power recovery once timing is converged, allowing it to downsize cells on non-critical paths and pick higher-Vt variants for leakage savings — a natural complement to hold fixing, since slowing a path and saving leakage often go hand in hand. You can also cap the number of hold buffers the engine is allowed to insert, which prevents a runaway insertion that bloats area and routing. The discipline of incremental passes deserves emphasis. A monolithic re-optimization can move cells the previous pass had carefully settled, causing the design to thrash between setup and hold without converging. Running incremental setup recovery, then a bounded hold pass, then re-checking, lets each correction settle before the next begins. Protecting the clock is non-negotiable. The practical guardrails are: mark all clock-tree cells and nets as don't-touch before optimization; define don't-touch regions around hard macros and pre-placed structures so the engine routes and buffers around them; and forbid the optimizer from inserting datapath logic inside the clock-spine area where it would compete with clock buffers for placement and routing resources. If the optimizer is allowed to resize or relocate clock cells freely, it can re-balance the tree and silently change insertion delays — which means every downstream timing number is now describing a different clock than the one CTS signed off on.

Technical diagram

Managing the Setup/Hold Trade-off

The defining tension of post-CTS optimization is that setup and hold pull in opposite directions on the same path. Adding delay to fix hold lengthens the data path — which eats directly into setup slack. Removing delay or upsizing to fix setup speeds the path up — which can open a hold violation. An engine that fixes hold too aggressively can convert a comfortably-passing setup path into a new setup failure, and the design ends up chasing its own tail. The healthy mental model is slack-aware fixing. Hold fixes should target paths that genuinely violate, with just enough delay to reach a small positive target slack — not a generous over-correction. Where a path has abundant setup margin, spending some of it on hold is cheap; where setup is already tight, hold must be fixed by other means (useful skew, a slower-Vt downsize that the setup path can absorb, or relocating the fix to a different point on the path). The worst outcome is blanket hold-buffer insertion that ignores the setup picture entirely. Useful skew is the most elegant escape from this trade-off because it does not lengthen or shorten the data path at all — it shifts the clock edge instead, buying setup margin on one path while paying for it with hold margin on another that can afford it. Used well, it closes violations that pure data-path edits cannot. Used carelessly, it ripples skew changes through the tree and destabilizes paths you were not even looking at. That is why useful skew at this stage is applied surgically and re-verified, never sprinkled broadly. A final caution: always re-run both setup and hold reports after any fixing pass, across the relevant corners. A change made to satisfy hold at one corner can degrade setup at another, and the propagated clock makes these cross-corner interactions sharper than they ever were under the ideal

model.

Interview Q&A

Q
Why do hold violations suddenly appear after CTS when they were rare before? Before CTS

the clock is ideal — zero skew, zero insertion delay — so launch and capture registers are assumed to clock simultaneously and most hold checks pass trivially. After CTS the clock is propagated, so registers see real skew. A short data path between two registers whose clock edges arrive at slightly different times can now fail hold. Real skew is what exposes hold as a first-class problem.

Q
How do you fix a hold violation, and why is the fix direction "add delay"? A hold violation

means data reaches the capture register too soon after the clock edge — it changes before the register has finished sampling the previous value. The fix is to delay the data so it arrives after the capture window, typically by inserting delay/buffer cells on the path, downsizing a driver, or swapping to a slower-Vt cell. You can also use useful skew to move the capture edge earlier. The direction is always toward more data-path delay.

Q
What does it mean to "protect the clock tree" during post-CTS optimization, and why does

it matter? It means marking clock cells and nets as don't-touch and fencing off clock regions so the data-path optimizer cannot resize, relocate, or buffer them. It matters because CTS balanced the tree to specific insertion delays and skews, and the timing analysis is built on those exact values. If the optimizer alters clock cells, it silently re-balances the tree and changes insertion delays — making every downstream timing number describe a clock the design never actually signed off on.

Q
How do you avoid over-fixing hold and hurting setup? Fix hold in a slack-aware way: target

only paths that truly violate, add just enough delay to reach a small positive target slack rather than over-correcting, and prefer fixes that the path's setup margin can absorb. On tight-setup paths, use useful skew or a downsize the path can tolerate instead of brute-force buffer insertion. Run incremental passes so each fix settles before the next, and always re-report both setup and hold across corners afterward, because adding hold delay directly consumes setup slack.

Key Takeaways

  • Post-CTS optimization runs against a propagated clock, so the engine finally sees real skew and insertion delay rather than the ideal-clock fiction.
  • Hold becomes a first-class concern because real skew exposes short data paths that were invisible pre-CTS; the fix is always to add data-path delay.
  • The fix menu spans setup recovery (upsize, restructure), hold fixing (delay cells, downsize, slower- Vt, useful skew), and transition/capacitance cleanup.
  • Useful skew is a genuine new lever — it trades margin between paths by moving clock edges instead of editing the data path.
  • Protecting the clock tree with don't-touch and fenced regions is non-negotiable; touching clock cells silently changes insertion delays and invalidates the timing picture.
  • Setup and hold pull in opposite directions; fix hold just enough, prefer incremental passes, and always re-verify both constraints across corners after every pass.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Replying to