Module 514 min

Timing Constraints (SDC)

The SDC timing contract: clocks (create_clock, generated, virtual), input/output delay and the I/O budget, uncertainty and latency (pre/post-CTS), exceptions, clock groups and case analysis.

Pro Tip

Synthesis has no idea how fast your chip must run until you tell it. The SDC (Synopsys Design Constraints) file is where you state the timing requirements: how fast the clock is, when data arrives at the inputs and is required at the outputs, and which paths are special. Every timing number the tool reports is computed against this file, so constraints that are wrong or missing produce a netlist that is optimised for the wrong thing. SDC is the single most important input to both synthesis and STA.

What SDC constrains

An SDC answers four questions for the timing engine: what are the clocks and how fast do they run; when does data arrive at, and when must it be valid at, the chip's ports; how much margin to hold back for real-world clock imperfection; and which paths should be timed differently or not at all. Get these right and the tool optimises the paths that matter; get them wrong and it wastes effort, or, worse, leaves real paths unchecked.

Defining the clock, create_clock

Everything starts with the clock. create_clock names a clock, sets its period (the frequency target) and its waveform (duty cycle / edges), and attaches it to a source, usually an input port or a pin. A 500 MHz target is simply create_clock -period 2.0 -name clk [get_ports clk]. Until a clock exists, no register-to-register path has a requirement, so nothing is really being timed.

Generated and virtual clocks

Not every clock comes from a port. A create_generated_clock is derived on-chip from a master clock, a divide-by-2, a clock-gate output, a multiplexed clock, and the tool needs to know its relationship to the master so it times the crossing correctly. A virtual clock has no physical source at all; it exists only to be the reference for I/O delays when the real clock lives outside the block. Both are common and both are easy to forget, which silently mis-times whole regions.

I/O timing, input and output delay

Your block does not live alone: some other logic drives its inputs and some other logic consumes its outputs. You model that external world with set_input_delay and set_output_delay, both stated relative to a clock. Input delay says how much of the period is already used up by upstream logic before data reaches your input port; output delay says how much the downstream logic needs before its own capture edge. What is left in the middle is the time your combinational logic actually has.

Input and output delay reserve the ends of the clock period; the green window in the middle is all your logic gets — click to enlarge

Each uses -max for the setup (long-path) check and -min for the hold (short-path) check. A missing I/O constraint is dangerous precisely because it fails quietly: an unconstrained input or output path is simply not checked, so it looks clean in the report while being wide open in silicon.

Note

Model the driver and the load too, set_driving_cell gives input ports a realistic driver (so input transition is real, not ideal) and set_load puts realistic capacitance on output ports. Without them the tool assumes perfect edges and under-buffers the boundary.

Clock uncertainty and latency

Real clocks are not perfect, so you subtract a margin. set_clock_uncertainty holds back time for skew and jitter. Before CTS you set it generously (it stands in for the skew you have not built yet plus jitter); after CTS you switch to set_propagated_clock so the tool uses the real, built clock latencies and only jitter plus a small margin remain. set_clock_latency separately models source latency (before the clock root, e.g. the PLL and top route) and network latency (the on-chip tree).

Pro Tip

Pre-CTS vs post-CTS, before the clock tree exists, use an ideal clock with a pessimistic set_clock_uncertainty to cover the unknown skew. Once CTS is done, set_propagated_clock makes timing use the actual insertion delays, and you drop the skew portion of uncertainty. Forgetting to propagate leaves you signing off on a fake, zero-latency clock.

Timing exceptions

Not every path that exists is a path that matters, and telling the tool so stops it chasing timing that is not real. The three exceptions you will use constantly:

  • set_false_path, a path that never carries real timing-critical data (e.g. a static config register, or a true asynchronous crossing). The tool stops timing it entirely.
  • set_multicycle_path, a path that is allowed more than one clock cycle to settle. You tell the tool how many cycles, and it relaxes the requirement accordingly.
  • set_max_delay / set_min_delay, a direct point-to-point delay override, used when neither a false nor a multicycle path fits (often on asynchronous or custom interfaces).

Clock groups

When a design has several clocks that are unrelated, say a 500 MHz core clock and a 100 MHz peripheral clock with no fixed phase relationship, timing paths between them are meaningless and should not be analysed. set_clock_groups -asynchronous tells the tool these clocks never time against each other (those crossings are handled by CDC synchronizers instead), and -physically_exclusive / -logically_exclusive handle clocks that can never be active at the same time, like a functional clock and a test clock on the same pin.

Case analysis

set_case_analysis pins a constant onto a signal, typically a mode or test-enable pin, so the tool analyses only the mode you actually run in. Setting a scan-enable to 0, for example, tells STA to time the functional mode and ignore the scan paths that are irrelevant in normal operation.

SDC command reference

CommandModelsExample
create_clockA clock: period, waveform, sourcecreate_clock -period 2.0 [get_ports clk]
create_generated_clockA derived/divided/gated clockcreate_generated_clock -divide_by 2 ...
set_input_delayUpstream arrival at an input portset_input_delay -max 0.8 -clock clk [ports]
set_output_delayDownstream requirement at an outputset_output_delay -max 0.7 -clock clk [ports]
set_clock_uncertaintySkew + jitter marginset_clock_uncertainty -setup 0.1 [clk]
set_clock_latencySource / network insertion delayset_clock_latency -source 0.3 [clk]
set_propagated_clockUse real (post-CTS) clock latenciesset_propagated_clock [all_clocks]
set_false_pathExclude a path from timingset_false_path -from [cfg_reg]
set_multicycle_pathAllow more than one cycleset_multicycle_path 2 -setup -to [ports]
set_max_delay / set_min_delayPoint-to-point delay overrideset_max_delay 1.5 -from A -to B
set_clock_groupsUnrelated / exclusive clocksset_clock_groups -asynchronous -group {..}
set_driving_cell / set_loadRealistic input driver / output loadset_load 0.05 [get_ports data_out]
set_case_analysisA constant on a mode/test pinset_case_analysis 0 [get_ports scan_en]
Watch out

Garbage constraints, garbage timing. An over-tight clock makes the tool over-build the whole design for slack you will never use; a missing I/O or exception constraint leaves real paths unchecked so the report looks green while silicon fails. Constrain honestly and completely, the SDC is the one file every downstream tool trusts absolutely.

Note

Recap: SDC is the timing contract: define clocks (create_clock, generated, virtual), model the outside world with input/output delay and driving-cell/load, reserve margin with uncertainty and propagate the clock after CTS, and clean up the analysis with exceptions, clock groups and case analysis. Every timing number depends on it.