← Home Timing & Power Optimization
6 Timing & Power Optimization

Useful Skew & Clock Skewing for Setup

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

Why Skew Belongs in the Optimization Chapter

Most engineers first meet the word skew in a clock-tree-synthesis (CTS) context, where the goal is usually to minimize the spread of clock arrivals so that every flip-flop sees the active edge at nearly the same instant. That framing is correct for CTS, but it is only half the story. In a timing-and-power optimization flow, skew is not merely a quantity to be suppressed — it is a lever you can pull deliberately to close setup violations that resist conventional gate sizing and buffering. This chapter treats clock skewing as an optimization transform. A companion CTS book would tell you how the physical tree is built and balanced; here we care about why an optimizer chooses to make one register's clock arrive a little later or a little earlier, when that choice beats restructuring the logic, and how you keep the technique from quietly creating hold problems or robustness risks downstream.

Technical diagram

Recap: What Useful Skew Actually Does

Consider two flip-flops connected by a combinational path. The setup check in its simplest form is:

launch_clock_arrival + clk_to_q + path_delay + setup
<= capture_clock_arrival + clock_period

Rearranged, the available setup slack is:

slack = (capture_arrival - launch_arrival) + period - (clk_to_q + path_delay + setup)

The term (capture_arrival - launch_arrival) is the skew between the two clock endpoints. If you make the capture clock arrive later (positive skew toward capture), you add directly to the setup slack of that stage. The catch is that the capture flop is also the launch flop for the next stage. Pushing its clock later borrows time from the downstream stage and hands it to the upstream stage. This is why the technique is also called time borrowing across register boundaries — you are not creating time, you are redistributing it from stages that have slack to spare into stages that are starved. The optimizer's job is to find a global assignment of small per-register clock offsets that maximizes the worst negative slack (and the total negative slack) across the whole design, subject to constraints. When done well, a chain of three stages with slacks of +200ps / -150ps / +180ps can be rebalanced toward something like +30ps / +20ps / +30ps , turning a failing endpoint into a passing one without touching a single logic gate.

When the Optimizer Reaches for Skew

A setup-recovery optimizer typically has an ordered toolbox: cell sizing (upsizing critical cells, downsizing off-critical ones), buffer insertion and removal, logic restructuring, pin swapping, layer/via promotion for net delay, and useful skew. Skew is usually applied after the cheaper local fixes have been tried and interleaved with them rather than as a final separate pass. The interaction matters. If the optimizer skews a register first and then sizes the surrounding logic, the two transforms can fight: a later sizing change alters path delay and invalidates the skew the optimizer

chose. Mature flows therefore co-optimize — they evaluate skew as one candidate move among sizing and buffering moves, keep a consistent timing model, and re-cost the clock latency assumptions whenever a path is modified. The practical consequence for you, the user, is that useful skew should be enabled during the optimization step, not bolted on afterward, so the engine accounts for it while making every other decision.

Technical diagram

Skew is most attractive when:

  • A path is balanced-but-tight: it is close to passing and the adjacent stage has slack to lend.
  • The violating path has no good local fix — it is already minimum depth and the cells are near maximum drive.
  • The fix needs to be area- and power-cheap, because skew costs only a small clock-latency change rather than added logic.

Controlling Useful-Skew Effort

Optimizers expose an effort knob for skew because aggressive skewing is powerful but risky. Higher effort means the engine explores larger per-register offsets, longer borrowing chains, and more endpoints, which finds more closure but also widens the window in which hold and variation must be re-verified. Here is a representative effort ladder and what each level implies:

Effort

What the engine does Typical use

Off / noneNo deliberate skew; clock treated as ideallyEarly floorplanning, or when hold budget is
balancedextremely tight
LowSmall offsets on a few worst endpoints onlyFinal convergence with minimal risk
MediumModerate offsets, short borrowing chains, broadDefault for most setup-recovery passes

endpoint coverage

High Large offsets, long multi-stage borrowing chains Aggressive WNS recovery on a stubborn A generic way to turn the lever on and dial it in:

# illustrative — generic, not tool-specific
set_optimization_option -useful_skew true
set_optimization_option -useful_skew_effort medium
optimize_timing -setup

The first line enables skew as a candidate transform inside optimization; the second sets how hard the engine searches; the third runs the setup-recovery pass that may now exploit skew alongside sizing and buffering.

Applying Skew Limits So Hold and Variation Stay Safe

Unbounded skew is dangerous. The same offset that buys setup margin on one path erodes hold margin on the path leaving that register, and large clock-latency differences amplify on-chip variation (OCV) because longer, more divergent clock paths share less common-path and accumulate more derate. The defense is to bound the per-register offset and to bound how much the engine may borrow per stage.

# illustrative — generic, not tool-specific
set_useful_skew_options -max_skew 80ps        ;# cap per-register offset magnitude
set_useful_skew_options -max_borrow 60ps      ;# cap time borrowed across one
boundary
set_useful_skew_options -protect_hold true    ;# forbid moves that push hold below
margin

A few principles for choosing these numbers:

  • Keep max_skew a fraction of the clock period. A common starting point is well under 10% of the period, tightened further on blocks with heavy hold pressure.
  • Reserve a hold guard band. The engine should refuse any skew that drives a hold path below your chosen positive-hold margin, so post-skew hold fixing does not explode.
  • Account for variation up front. Set limits against the derated (OCV / signoff-style) timing view, not the nominal view, so margin survives at the corner where it is actually checked. Crucially, apply the same limits consistently across the whole flow — placement-stage optimization, post-CTS optimization, and routing-stage optimization should all honor the same skew and borrow caps. If early stages skew aggressively under loose limits and later stages enforce tight ones, the later engine spends its budget undoing the earlier work, and the design oscillates instead of converging. Set the policy once and propagate it.

The Setup-vs-Hold Tension, and How the Flow Protects Hold

Every picosecond of capture-side delay you add for setup is a picosecond removed from the hold check on paths that launch from that same register. Formally, the hold check for a path is:

launch_arrival + clk_to_q + path_delay
>= capture_arrival + hold

When you push a register's clock later to help its incoming setup path, that register becomes the launch point with a later launch_arrival , which helps hold on its outgoing paths — but the registers it feeds now have a relatively earlier capture clock, which hurts their hold. Skew never improves both checks everywhere; it trades margin spatially across the register graph. A well-behaved flow protects hold in two layers:

  1. 1. During skew selection, the engine refuses moves that violate the hold guard band (the -

protect_hold style option). This keeps the damage bounded.

  1. 2. After setup-driven skew is fixed, a dedicated hold-fix pass inserts delay buffers (or uses slower

cell variants / longer routes) to repair the residual hold violations the skew introduced. Because skew was bounded, this cleanup is cheap; without bounds, hold buffering can balloon in area and leakage. The ordering is deliberate: fix setup (including via skew) first, lock the clock-latency decisions, then close hold against those frozen latencies. Re-skewing after hold closure is avoided because it would invalidate the hold buffers just inserted.

Technical diagram

Where Skewing Helps vs. Where Logic Transforms Are Better

Skew and logic restructuring solve different shapes of problem. Skew redistributes a fixed amount of delay across stage boundaries; restructuring reduces the delay of a stage outright. If a path is genuinely too long — too many logic levels, a slow function, a late-arriving control signal feeding deep logic — no amount of borrowing will save it, because the neighbors do not have enough slack to lend and you will simply push the violation next door. Use this symptom-to-lever table as a triage guide:

SymptomBetter leverWhy
Path is near-passing, neighborUseful skew stage has slackCheap borrow closes a small gap with no logic change
Adjacent stages alternate large +Useful skew and − slackClassic rebalancing case; skew flattens the profile
Logic depth far exceeds theRestructure / re-pipeline period budgetBorrowing cannot remove levels of logic
Late-arriving signal drives deepRestructure (move late signalSkew on the endpoint cannot fix an
combinational conecloser to output)internal long pole
High fanout net dominates theBuffer / clone / size pathNet delay, not register timing, is the bottleneck
Single isolated tight path amongUseful skew loose neighborsBorrowing from loose neighbors is exactly the right move
Whole region is uniformlyRestructure / re-architectNo slack anywhere to borrow from

negative The rule of thumb: skew is a redistribution tool, so it only works when slack exists somewhere nearby to redistribute. When the entire neighborhood is failing, you have a logic or architecture problem, and skew will just shuffle the violation around the block.

Putting It Together: A Bounded Skew-Enabled Setup Pass

Tying the pieces into one coherent invocation, with the validity of each construct flagged:

# Constraints that the optimizer must read (timing intent)
create_clock -name CLK -period 2.0 [get_ports clk]      # standard (SDC)
set_clock_uncertainty -setup 0.05 [get_clocks CLK]      # standard (SDC)
set_clock_uncertainty -hold  0.03 [get_clocks CLK]      # standard (SDC)
# Enable and bound useful skew as an optimization transform
set_optimization_option -useful_skew true               # illustrative — generic, not
tool-specific
set_optimization_option -useful_skew_effort medium      # illustrative — generic, not
tool-specific
set_useful_skew_options -max_skew 0.15 -max_borrow 0.12 # illustrative — generic, not
tool-specific
set_useful_skew_options -protect_hold true              # illustrative — generic, not
tool-specific
# Run setup recovery; skew is now one move among sizing/buffering
optimize_timing -setup                                  # illustrative — generic, not
tool-specific
# Freeze latencies, then repair hold against them
optimize_timing -hold                                   # illustrative — generic, not
tool-specific
Note

Note the discipline: the intent (clock definition, uncertainty) is expressed in standard SDC; the engine

behavior (enabling skew, effort, limits, run order) is expressed in tool-style commands whose exact spelling varies, hence the illustrative comment. In an interview, calling out which commands are portable SDC and which are vendor-shaped tool options signals real flow maturity.

Interview Q&A

Q
Why does useful skew not increase the total available time in a design, and what does it

actually do? Useful skew redistributes slack across register boundaries rather than creating it. Pushing a capture clock later adds margin to its incoming setup path but removes the same time from the outgoing paths of that register, because that register launches the next stage. It is a borrowing operation: tight stages take time from neighboring loose stages. It only succeeds when slack exists nearby to borrow; if a whole region is negative, skew merely moves the violation around.

Q
Your optimizer fixed setup using aggressive skew, and now hold is badly violated and the

hold-fix buffering is huge. What went wrong and how do you prevent it? The skew limits were too loose, so large per-register offsets stole hold margin from downstream paths. Prevent it by bounding skew up front: cap the per-register offset and the per-boundary borrow to a small fraction of the period, enable a hold guard band so the engine refuses moves that drive hold below margin, and evaluate those limits against the derated/OCV view rather than nominal. Also apply the same limits consistently across placement, post-CTS, and route stages so later passes do not have to undo earlier aggression.

Q
When would you choose logic restructuring over useful skew for a setup violation? Choose

restructuring when the path is too long in absolute terms — excessive logic depth, a slow function, or a late-arriving signal feeding a deep cone — and when the neighboring stages lack the slack to lend.

Skew cannot remove levels of logic; it only redistributes a fixed delay budget. If the whole neighborhood is negative, or a single internal long pole dominates, restructure or re-pipeline. Reserve skew for near-passing paths sitting next to stages with spare slack.

Q
Why should useful skew be enabled during optimization rather than applied as a separate

pass afterward? Because skew interacts with sizing and buffering. If skew is chosen first and the logic is later resized, the path delay changes and the chosen skew becomes wrong. Co-optimizing lets the engine treat skew as one candidate move among sizing, buffering, and restructuring, costed against a consistent timing model and re-evaluated whenever a path changes. A bolted-on skew pass cannot see those downstream sizing decisions and tends to leave the design in an inconsistent, oscillating state.

Key Takeaways

  • Useful skew is an optimization lever, not just a CTS quantity to minimize: it borrows time from loose stages into tight ones by adjusting per-register clock arrivals.
  • It redistributes slack rather than creating it; it works only when neighboring stages have slack to lend.
  • Enable it inside the optimization step so it co-optimizes with sizing and buffering, not as a disconnected pass.
  • Control it with an effort knob, but always bound per-register skew and per-boundary borrow, evaluated against the derated view, with a hold guard band.
  • Apply the same skew limits consistently across the whole flow to avoid later stages undoing earlier work.
  • Respect the setup-vs-hold tension: fix setup with bounded skew first, freeze clock latencies, then repair hold — and do not re-skew afterward.
  • Reach for restructuring instead of skew when paths are too long in absolute terms or the whole region is failing.

Comments

Leave a Reply

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

Replying to