HomeSTA CourseDay 4
DAY 4 · STA FUNDAMENTALS

SDC Constraints — The Language of Timing

By EcrioniX · Updated June 2026

Without constraints, STA produces nothing useful. The SDC (Synopsys Design Constraints) file is what you tell the tool about your design’s timing requirements: what the clocks are, how fast they run, how much time the I/O ports consume, and which paths are exceptions. Getting SDC right is as important as getting the RTL right — a wrong constraint is worse than a missing one, because it silently hides real violations.

1. What SDC is

SDC is a Tcl-based constraint format originally developed by Synopsys and now the universal standard across all EDA tools — PrimeTime, Tempus, DesignCompiler, Genus, Vivado, Quartus. An SDC file is a Tcl script that runs inside the STA tool. Every command in it queries or modifies the tool’s timing database.

The STA tool reads the SDC after loading the netlist and libraries. Without SDC, the tool has no idea what the clock period is, what the I/O interface timing budget looks like, or which paths between async domains should be excluded. The quality of your STA results is directly proportional to the accuracy of your SDC.

2. Defining clocks — create_clock

Every STA session starts with defining clocks. create_clock tells the tool: “this port is a clock, it has this period, and here is its waveform.”

create_clock — basic to advanced
## Basic: 500 MHz clock on port 'clk' (period = 2 ns, 50% duty cycle)
create_clock -name clk_core -period 2.0 [get_ports clk]

## Custom waveform: rises at 0.0 ns, falls at 0.8 ns (40% duty cycle)
create_clock -name clk_io -period 4.0 -waveform {0.0 0.8} [get_ports clk_io]

## Generated clock: divided by 2 from clk_core
create_generated_clock -name clk_div2 \
  -source [get_ports clk] \
  -divide_by 2 \
  [get_pins clk_div_reg/Q]

## PLL output clock (source is the PLL output pin)
create_clock -name clk_pll -period 1.0 [get_pins pll_inst/CLKOUT]

Never create clocks on internal pins unless necessary

Create clocks on ports (primary inputs), not internal gates. The only exception is when a clock is genuinely generated inside the chip (PLL, clock divider). Creating clocks on arbitrary internal pins causes STA to analyse every downstream path as if a new clock source exists there, often producing thousands of false violations.

3. I/O constraints

Paths from primary inputs and to primary outputs need external timing information. The chip does not exist in isolation — it connects to other chips with their own propagation delays.

set_input_delay and set_output_delay
## Input delay: external chip drives our input 1.2 ns after the clock edge
## This leaves (period - input_delay - setup_time) for internal logic
set_input_delay -max 1.2 -clock clk_core [get_ports data_in]
set_input_delay -min 0.4 -clock clk_core [get_ports data_in]

## Output delay: downstream chip needs data 0.8 ns before its clock edge
set_output_delay -max 0.8 -clock clk_core [get_ports data_out]
set_output_delay -min 0.1 -clock clk_core [get_ports data_out]

## Multiple ports at once
set_input_delay -max 1.5 -clock clk_core [get_ports {addr[*] data[*]}]
Constraint-max (setup)-min (hold)
set_input_delayReduces time budget for internal logic (setup analysis)Sets minimum data launch time (hold analysis)
set_output_delayReduces time budget before output must be valid (setup)Sets minimum stable time after clock at output (hold)

4. Timing exceptions

Real designs have paths that STA should not check with the default single-cycle constraint. These must be declared explicitly.

set_false_path

Removes a path from timing analysis entirely. Use when a path is physically impossible to activate, or when it crosses asynchronous domains where timing is handled by a synchroniser.

set_false_path examples
## Async clock domain crossing (handled by synchronizer — no timing relationship)
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]

## Reset path (async reset, not timing-critical in this direction)
set_false_path -from [get_ports rst_n]

## Test mode only path (never active in functional mode)
set_false_path -through [get_pins test_mux/S]

## Bidirectional: both A→B and B→A are false
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]
set_false_path -from [get_clocks clk_b] -to [get_clocks clk_a]

set_multicycle_path

Relaxes the timing requirement for a path to N clock cycles. Used when a computation legitimately takes more than one cycle (e.g. a divide unit, a floating-point multiplier that is only read every N cycles).

set_multicycle_path — correct pattern
## Path through slow_unit gets 2 cycles for setup
set_multicycle_path 2 -setup -from [get_cells slow_unit/*] \
                             -to   [get_cells result_reg/*]

## MANDATORY: adjust hold back by (N-1) cycles
## Without this, hold is checked at cycle 2 which is wrong
set_multicycle_path 1 -hold  -from [get_cells slow_unit/*] \
                             -to   [get_cells result_reg/*]

## 3-cycle path (e.g. FP multiplier pipeline read-out)
set_multicycle_path 3 -setup -from [get_cells fpmul/*] -to [get_cells accum_reg/*]
set_multicycle_path 2 -hold  -from [get_cells fpmul/*] -to [get_cells accum_reg/*]

Always pair set_multicycle_path setup with a hold adjustment

When you set -setup N, STA checks the path against the Nth cycle. But hold is still checked at cycle 1 by default, which is often pessimistic or wrong. The standard rule: -hold (N-1) to match. Forgetting this produces phantom hold violations that waste timing closure time.

5. Clock groups

When two clocks have no defined phase relationship (asynchronous), you must tell STA not to check paths between them. set_clock_groups is cleaner than per-path set_false_path for this.

set_clock_groups
## Two completely independent clock domains (e.g. core and USB)
set_clock_groups -asynchronous \
  -group [get_clocks clk_core] \
  -group [get_clocks clk_usb]

## Three-domain chip: core, DDR, PCIe all asynchronous to each other
set_clock_groups -asynchronous \
  -group [get_clocks clk_core] \
  -group [get_clocks clk_ddr] \
  -group [get_clocks clk_pcie]

## Exclusive clocks (only one active at a time, e.g. functional vs scan)
set_clock_groups -exclusive \
  -group [get_clocks clk_func] \
  -group [get_clocks clk_scan]

6. Clock uncertainty

Real clocks are not perfectly periodic. Clock uncertainty models jitter (cycle-to-cycle variation), plus margin for process variation and any skew not yet accounted for by the clock tree.

set_clock_uncertainty
## Pre-CTS: include estimated skew + jitter (larger value)
set_clock_uncertainty -setup 0.2 [get_clocks clk_core]
set_clock_uncertainty -hold  0.05 [get_clocks clk_core]

## Post-CTS: real tree delays are now in SPEF; only jitter remains
set_clock_uncertainty -setup 0.08 [get_clocks clk_core]
set_clock_uncertainty -hold  0.05 [get_clocks clk_core]

## Inter-clock uncertainty (different clocks from same PLL)
set_clock_uncertainty -setup 0.1 \
  -from [get_clocks clk_core] -to [get_clocks clk_div2]

7. Load and drive constraints

Drive, load, and transition constraints
## Set output load (capacitance of external pin in fF)
set_load 50 [get_ports data_out]
set_load 200 [get_ports {bus_out[*]}]

## Set drive strength of input ports (drive resistance in kohm)
set_driving_cell -lib_cell BUFX4 -library sc9_cln28hp \
  [get_ports data_in]

## Limit max transition time on all output ports (ns)
set_max_transition 0.4 [get_ports {*}]

## Limit max fanout on internal nets
set_max_fanout 16 [current_design]

8. A complete realistic SDC

soc_top.sdc — complete example
############################################################### ## SoC Top-Level SDC ## Technology: TSMC 28nm HP | Frequency: 500 MHz (clk_core) ############################################################### ## ── 1. Clock Definitions ────────────────────────────────── create_clock -name clk_core -period 2.0 [get_ports clk_core] create_clock -name clk_usb -period 16.667 [get_ports clk_usb] create_clock -name clk_ddr -period 1.25 [get_ports clk_ddr] create_generated_clock -name clk_div2 \ -source [get_ports clk_core] -divide_by 2 \ [get_pins clk_div_inst/Q] ## ── 2. Clock Groups (async domains) ─────────────────────── set_clock_groups -asynchronous \ -group {clk_core clk_div2} \ -group {clk_usb} \ -group {clk_ddr} ## ── 3. Clock Uncertainty ────────────────────────────────── set_clock_uncertainty -setup 0.08 [get_clocks clk_core] set_clock_uncertainty -hold 0.05 [get_clocks clk_core] set_clock_uncertainty -setup 0.12 [get_clocks clk_usb] set_clock_uncertainty -hold 0.05 [get_clocks clk_usb] ## ── 4. I/O Constraints ──────────────────────────────────── set_input_delay -max 0.6 -clock clk_core [get_ports {data_in[*]}] set_input_delay -min 0.1 -clock clk_core [get_ports {data_in[*]}] set_output_delay -max 0.5 -clock clk_core [get_ports {data_out[*]}] set_output_delay -min 0.0 -clock clk_core [get_ports {data_out[*]}] ## ── 5. Timing Exceptions ────────────────────────────────── ## Async reset path set_false_path -from [get_ports rst_n] ## Scan mode (only one mode active at a time) set_clock_groups -exclusive \ -group [get_clocks clk_core] \ -group [get_clocks clk_scan] ## FP multiplier: 3-cycle multicycle path set_multicycle_path 3 -setup \ -from [get_cells fpmul_inst/*] -to [get_cells result_reg/*] set_multicycle_path 2 -hold \ -from [get_cells fpmul_inst/*] -to [get_cells result_reg/*] ## ── 6. Physical Constraints ─────────────────────────────── set_load 50 [get_ports {data_out[*]}] set_driving_cell -lib_cell BUFX8 [get_ports {data_in[*]}] set_max_transition 0.3 [current_design]

9. SDC command quick reference

Clock commands

create_clock

Define primary clock from port or pin

create_generated_clock

Define derived clock (divide, multiply, invert)

set_clock_uncertainty

Add jitter and skew margin

set_clock_latency

Override clock network delay

I/O commands

set_input_delay

Time consumed by external logic before our input

set_output_delay

Time needed by external logic after our output

set_driving_cell

Drive strength of input ports

set_load

Capacitive load on output ports

Exception commands

set_false_path

Exclude path from all timing checks

set_multicycle_path

Allow N cycles for setup/hold

set_max_delay

Override max path delay directly

set_min_delay

Override min path delay directly

Domain commands

set_clock_groups

Define async or exclusive clock groups

set_max_transition

Limit signal slew rate

set_max_fanout

Limit number of receivers per net

set_case_analysis

Tie signals to constant for analysis mode

SDC is evaluated top-to-bottom; later commands override earlier ones

If you write set_false_path -from clk_a -to clk_b and then later write set_multicycle_path 2 -from clk_a -to clk_b, the multicycle path wins for those specific paths. Order matters — exceptions follow last-writer-wins semantics within the same scope.

Day 4 Key Takeaways

Frequently Asked Questions

What is an SDC file?

SDC (Synopsys Design Constraints) is a Tcl-based format for specifying timing constraints. It defines clocks, I/O delays, timing exceptions, and clock relationships. Without SDC, STA cannot produce meaningful results. SDC is the universal standard — accepted by PrimeTime, Tempus, DesignCompiler, Genus, Vivado, and Quartus.

What does create_clock do?

It defines a clock in the design: the source port or pin, the period in nanoseconds, and the waveform (rise and fall edges). STA uses this to compute the required arrival time at every flip-flop driven by that clock. Without create_clock, paths involving that clock are unconstrained and ignored.

What is set_false_path?

It removes a path from STA analysis. Used for paths between asynchronous clock domains, test-mode-only paths, or paths that are functionally impossible. A wrong false path declaration masks real violations — every false path must have a documented functional justification.

Why must set_multicycle_path always include a hold adjustment?

When you declare set_multicycle_path N -setup, STA checks setup at cycle N but still checks hold at the default (cycle 1). This produces incorrect hold analysis. The standard fix: always follow with set_multicycle_path (N-1) -hold on the same path specification.

What is the difference between set_false_path and set_clock_groups -asynchronous?

set_clock_groups -asynchronous tells STA that two clock domains have no phase relationship — all paths between them are unconstrained (neither checked nor reported as violations). set_false_path excludes specific paths from analysis but still allows other paths between the same clocks to be checked. For truly async domains, set_clock_groups is cleaner and less error-prone.

← Previous
Day 3: Timing Paths