Cheat sheet

XDC and SDC constraints cheat sheet

The timing and pin constraints you write every week in Vivado XDC and Quartus SDC, with Lattice equivalents, worked patterns and a table of common mistakes.

AMDAlteraLast verified 13 Nov 2026· Vivado 2026.1, Quartus Prime Pro 26.3, Radiant 2026.1

This sheet is for engineers who know what a constraint is for and need the exact syntax. Vivado reads XDC, which is SDC plus set_property. Quartus Prime reads SDC for timing and a separate QSF file for pins. Lattice Radiant reads SDC-like timing in a .pdc file, Diamond uses .lpf. Read the vendor manuals linked at the end for the full option lists.

Clocks

Task XDC (Vivado) SDC (Quartus Prime)
Primary clock on a pin create_clock -period 10.000 -name clk_in [get_ports clk_in] create_clock -period 10.0 -name clk_in [get_ports clk_in]
Clock with a 25 % duty cycle add -waveform {0 2.5} same
Clocks from a PLL or MMCM Derived automatically derive_pll_clocks (Standard) or automatic (Pro)
Manual generated clock create_generated_clock -name clk_div2 -source [get_pins u_div/clk] -divide_by 2 [get_pins u_div/q_reg/Q] same syntax
Clock on a recovered or external source create_clock -period 8.000 [get_pins u_gt/RXOUTCLK] same
Report what the tool found report_clocks report_clocks
Jitter and uncertainty set_clock_uncertainty -setup 0.100 [get_clocks clk_in] derive_clock_uncertainty (do this once, always)
# Vivado: a 100 MHz board clock and a clock-enable style divided clock
create_clock -period 10.000 -name sys_clk [get_ports sys_clk_p]
create_generated_clock -name clk_div4 -source [get_pins u_div/clk_i] \
    -divide_by 4 [get_pins u_div/div_reg[1]/Q]

Define a generated clock only for clocks made with logic (a toggling flip-flop). Clocks that come out of a PLL, MMCM or clock buffer are derived by the tool. Prefer clock enables over divided clocks in the first place.

I/O delays

External delays are specified relative to a clock. The tool then checks the path from pin to the first register (input) or from the last register to the pin (output).

Task XDC and SDC (same syntax)
Input, system-synchronous set_input_delay -clock sys_clk -max 2.5 [get_ports data_in[*]] and -min 0.5
Input, DDR, both edges add -clock_fall -add_delay for the falling-edge set
Output set_output_delay -clock sys_clk -max 1.0 [get_ports data_out[*]] and -min -0.5
Source-synchronous input create_clock on the forwarded clock pin, then set_input_delay -clock fwd_clk
Source-synchronous output create_generated_clock on the forwarded clock port, then set_output_delay -clock fwd_clk_out
# Source-synchronous ADC interface, data centred on the forwarded clock
create_clock -period 8.000 -name adc_clk [get_ports adc_clk]
set_input_delay -clock adc_clk -max 1.200 [get_ports adc_d[*]]
set_input_delay -clock adc_clk -min 0.300 [get_ports adc_d[*]]
set_input_delay -clock adc_clk -max 1.200 -clock_fall -add_delay [get_ports adc_d[*]]
set_input_delay -clock adc_clk -min 0.300 -clock_fall -add_delay [get_ports adc_d[*]]

The -max value is the latest the data can arrive after the clock edge (setup side) and -min is the earliest it can change (hold side). For outputs, -max is the external setup requirement and -min is minus the external hold requirement. See /reference/timing-formulae/ for the arithmetic.

Exceptions

Task Command Use when
Unrelated clock domains set_clock_groups -asynchronous -group [get_clocks clk_a] -group [get_clocks clk_b] Both domains cross through proper synchronisers
One path, ignore timing set_false_path -from [get_cells u_a/cfg_reg*] -to [get_cells u_b/sync_reg[0]] Static configuration bits sampled long after they settle
Reset release set_false_path -from [get_ports rst_n] Asynchronous input going into a reset synchroniser
Slow path, N cycles set_multicycle_path -setup 2 -from [get_cells a_reg*] -to [get_cells b_reg*] plus set_multicycle_path -hold 1 ... Data is only used every N cycles because of a clock enable
Bound a crossing set_max_delay -datapath_only 5.000 -from [get_cells src_reg*] -to [get_cells dst_reg*] (Vivado) Bus crossing where skew between bits matters
Bus skew set_bus_skew -from [get_cells gray_reg*] -to [get_cells sync_reg*] 2.000 (Vivado 2018.1 and later) Gray pointer crossings in async FIFOs
# Vivado: a two-cycle path driven by a clock enable
set_multicycle_path -setup 2 -from [get_cells u_dsp/acc_reg*] -to [get_cells u_dsp/out_reg*]
set_multicycle_path -hold  1 -from [get_cells u_dsp/acc_reg*] -to [get_cells u_dsp/out_reg*]

Quartus Prime accepts set_max_delay without -datapath_only. The clock skew is still counted, so pair it with set_false_path -hold or use set_net_delay for the same effect. In Quartus, hold multicycle values are counted differently from Vivado: -hold 1 here means “end minus one”, which usually matches what you want after -setup 2.

Pins and I/O standards

Task Vivado XDC Quartus QSF
Pin location set_property PACKAGE_PIN W5 [get_ports clk] set_location_assignment PIN_AF14 -to clk
I/O standard set_property IOSTANDARD LVCMOS33 [get_ports clk] set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to clk
Both at once set_property -dict { PACKAGE_PIN W5 IOSTANDARD LVCMOS33 } [get_ports clk] two lines
Differential pair IOSTANDARD LVDS_25 on the _p port; the _n pin follows assign both pins, standard LVDS
Pull-up set_property PULLUP true [get_ports btn] set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to btn
Drive and slew set_property DRIVE 8 ... and set_property SLEW FAST ... -name CURRENT_STRENGTH_NEW 8MA and -name SLEW_RATE 1
Unused pins set_property BITSTREAM.CONFIG.UNUSEDPIN PULLDOWN [current_design] set_global_assignment -name RESERVE_ALL_UNUSED_PINS_WEAK_PULLUP "AS INPUT TRI-STATED WITH WEAK PULL-UP"
Configuration voltage set_property CFGBVS VCCO [current_design] and CONFIG_VOLTAGE 3.3 not needed
# Vivado: a bus of eight LEDs, one line per pin
set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 } [get_ports { led[0] }]
set_property -dict { PACKAGE_PIN E19 IOSTANDARD LVCMOS33 } [get_ports { led[1] }]

Lattice equivalents

Task Radiant PDC Diamond LPF
Clock create_clock -period 12.0 -name clk [get_ports clk] FREQUENCY PORT "clk" 83.33 MHz ;
Pin ldc_set_location -site {A9} [get_ports clk] LOCATE COMP "clk" SITE "A9" ;
I/O type ldc_set_port -iobuf {IO_TYPE=LVCMOS33} [get_ports clk] IOBUF PORT "clk" IO_TYPE=LVCMOS33 ;
False path set_false_path -from [get_clocks a] -to [get_clocks b] BLOCK PATH FROM CLKNET "a" TO CLKNET "b" ;
Multicycle set_multicycle_path 2 -from ... MULTICYCLE FROM CELL "a*" TO CELL "b*" 2 X ;

The open-source flow uses a .pcf file for iCE40 (set_io led A5) and .lpf for ECP5 with nextpnr, with clock targets given on the command line or via set_frequency in the pre-pack script.

Mistakes and fixes

Symptom Likely cause Fix
No timing paths reported, everything passes No create_clock, so nothing is constrained Add clocks first, then run check_timing (Vivado) or look at the “unconstrained paths” report
Thousands of failing paths between two clocks you consider unrelated Missing set_clock_groups Declare the groups, after confirming every crossing has a synchroniser
Hold violations on I/O after a clean setup run Only -max delay given Always give -min too, including negative values for outputs
Setup failure on a path that only matters every other cycle Missing multicycle Add -setup N and -hold N-1 together, scoped by cells not by clocks
Timing passes but the board is flaky I/O delays guessed, not taken from the datasheet Rebuild the budget from the datasheet numbers and the board trace lengths
Constraint silently ignored Object name wrong after synthesis renamed it Use get_cells -hierarchical -filter, check with report_property, and keep constraints after the netlist exists
Generated clock reported as unconstrained -source points to the wrong pin Point -source at the clock input pin of the dividing register, and the target at its output
Vivado warns that set_max_delay has no effect Path already covered by a false path or clock group Remove the broader exception; the most specific exception wins only within a type
Pin assignment fails with a bank voltage conflict Mixed I/O standards in one bank Group standards by bank; check VCCO on the schematic
Quartus reports “no paths” for an SDC exception Pro uses full hierarchical names, Standard accepts wildcards differently Use get_registers with full paths in Pro

Sources