Skip to main content

Timing-Driven Synthesis

Authoring SDC Constraints established what goes into the constraint file. This page covers what actually happens once a synthesis tool reads it — the constraints aren't consulted once at the end to grade the result; they steer nearly every decision made throughout the flow, from the moment the SDC loads.

The constraint file is read first, not last​

The synthesis run's very first step, before any real optimization begins, is loading the SDC file. From that point forward, technology-independent optimization and technology mapping both make their decisions with the loaded clock period and I/O delays already in view — not as an afterthought applied to a finished network, but as the actual target every intermediate choice gets judged against.

How a tighter constraint concretely changes the result​

A synthesis tool responds to a tighter clock period, or a smaller input delay, in ways that are directly visible in the resulting netlist, not just in a timing report:

  • Less logic depth per pipeline stage. Multi-level factoring that would otherwise favor a smaller, deeper network gets biased toward a shallower one instead — fewer gate delays stacked between one register and the next, even if that costs more area.
  • Faster cell variants selected during mapping. Standard cell libraries provide multiple drive-strength options for the same logical function specifically for this — a path with real slack can use a small, low-power cell; a path the tighter constraint now makes critical gets mapped to a larger, faster variant of the identical function instead.
  • Different buffering and retiming choices. A path that comfortably met a looser constraint might need an inserted buffer to meet a tighter one — not because the logic changed, but because the timing budget available for that exact path shrank.

None of this is the tool "trying harder" uniformly across the whole design — it's the tool concentrating optimization effort specifically on whatever the current constraints say is critical, and spending comparatively little effort on paths with slack to spare.

A cheaper lever than any of the above: swapping which pin a signal lands on​

Not every timing fix requires a bigger cell, a deeper restructuring, or an inserted buffer. Many library cells (an AND2, an OR2) are logically commutative — A AND B equals B AND A — but the two input pins on the real, physical cell are rarely electrically identical, since each pin's intrinsic delay to the output can differ slightly based on its position inside the cell's transistor layout. Commutative pin swapping takes advantage of exactly this: if a critical signal happens to be wired to the slower of a commutative cell's two input pins, the tool can simply swap which pin it lands on — same logic, same cell, no added area — and pick up whatever small delay improvement the faster pin offers. It's a cheap, almost-free optimization compared to resizing or buffering, which is exactly why a real tool tries it before reaching for costlier fixes.

Telling the tool where not to spend effort: false paths and multicycle paths​

The constraints covered so far all say something is critical; two more SDC commands do the opposite — they tell the tool a path doesn't need to meet the default single-cycle timing budget at all, freeing up optimization effort for paths that genuinely do. set_false_path marks a path whose control signals can never actually enable it in real operation — a debug-mode-only path, or two mutually-exclusive configuration paths that can never both be active — so the tool stops trying to speed it up and stops flagging it as a violation. set_multicycle_path marks a path that's allowed more than one clock cycle to settle — deliberately slow logic (a multiplier feeding a register that only samples the result every third cycle, say) — rather than forcing the tool to squeeze it into a single-cycle budget it was never designed to need.

Both exceptions directly change where synthesis spends its effort: a path excluded this way stops competing for the faster cell variants and shallower-logic-depth treatment described above, freeing that budget for paths that actually need it. But the same exclusion is exactly why over-declaring a false path is a real risk, not just an optimization nicety — marking a path false when it can, in fact, occur in real operation hides a genuine timing bug rather than removing a non-issue, since neither synthesis nor STA will ever check it again. Each exception has to be backed by real understanding of the design's functional behavior, not just a convenient way to silence a stubborn violation. A path covered by either exception is also off-limits to register retiming across that boundary — the timing-driven repositioning Standard Cell Libraries-driven mapping can otherwise apply, since retiming a register through a path the tool has been told not to analyze would invalidate the very exception that path relies on.

The clock period the tool actually targets isn't quite the SDC number​

create_clock's period, from Authoring SDC Constraints, is the nominal target — but a real clock never arrives at every flip-flop at exactly that period, cycle after cycle, with zero variation. set_clock_uncertainty tells the tool to budget margin for that reality directly: it specifies an amount — covering jitter (cycle-to-cycle timing variation from the clock source itself) and skew (the clock arriving at slightly different times at different flip-flops) — that gets subtracted from the nominal clock period before the tool judges whether a path meets setup timing. A path that appears to meet a 2ns clock period exactly, with zero margin left over, isn't actually safe once set_clock_uncertainty accounts for real jitter and skew — timing-driven synthesis optimizes against this reduced, effective period, not the nominal one straight out of create_clock. This is exactly why synthesis timing tightens the moment a real clock's jitter/skew characteristics are known, even when the nominal clock period in the SDC file never changes: the number synthesis is actually driving against was always a little tighter than the number written in create_clock.

The same design, two different constraint files, two different netlists​

This is the concrete version of the claim Introduction opened with — that synthesis isn't compilation. Run identical RTL through synthesis twice, once with a loose clock period and once with a tight one, and the two resulting netlists are genuinely different: different cell choices on the same logical paths, different logic depth, sometimes different pipelining decisions entirely. A compiler's output doesn't change shape based on a performance target handed to it separately from the source code; a synthesis tool's does, by design — the constraint file is as much an input to the actual result as the RTL itself.

What's next​

Section B covered optimization and constraints — what shapes the netlist and what target it's shaped against. Section C turns to the actual tradeoffs those shaping decisions are made under, starting with the three-way pull every synthesis run balances: power, performance, and area.