FPGA Timing Constraints & Static Timing Analysis Guide
XDC/SDC, Clocks, Setup/Hold, I/O Timing, CDC & Timing Closure
1. Introduction — The Synthesis That Meets Timing Is the Design
A field-programmable gate array design is not finished when it synthesizes and fits — it is finished when it meets timing. Static timing analysis (STA) is the mathematical proof that every register-to-register path, every clock edge, and every I/O path can propagate data within the allowed time under worst-case process, voltage and temperature. Timing constraints are the contract you write for the tools: they describe the clocks, the false and multi-cycle paths, and the I/O timing so that the STA engine can verify (and the fitter can optimize) against reality instead of fantasy. This guide covers how to declare clocks correctly in XDC (Vivado) and SDC (common to Intel/others), what setup and hold analysis actually compute, how to constrain and fix asynchronous clock-domain crossings (CDC), how to handle I/O timing for external memories and buses, the common causes of timing failure, and the workflow that leads to timing closure. It deliberately complements the broader FPGA design guide on this site with the constraint-centric depth that standalone descriptions usually omit.
2. What STA Computes — Setup and Hold
Every synchronous path starts at a launch flip-flop and ends at a capture flip-flop. The data must arrive at the capture flop before the capture clock edge minus the setup time, and must not change too early after the edge (hold). The setup check compares the data arrival time with the required arrival time:
Setup slack = T_req − T_arr = (T_clk + T_cycle) − (T_launch + T_logic + T_data)
Hold slack = T_arr_hold − T_req_hold = (T_launch + T_logic) − (T_clk + T_hold)
Where T_logic is the combinational delay between the flops, T_data is the routing + register input delay, and T_clk is the clock skew. Setup is tested at the launch edge of cycle N and the capture edge of cycle N+1; hold is tested between edge N on both — which is why hold failures often bite on routing-delay changes, clock skew and late optimizations, and are invisible to simulation. The fitter reports worst-negative-slack (WNS), total-negative-slack (TNS) and worst-hold-slack (WHS); WNS > 0, TNS = 0 and WHS > 0 after implementation is the definition of timing closure.
3. Writing Correct Clock Constraints
3.1 The master clock
The most important constraint is the true definition of the clock source — not the virtual name you hope it is. In XDC:
create_clock -name clk_100m -period 10.000 -waveform {0 5.000} [get_ports clk_100m]
If the clock enters through a PLL or MMCM, constrain the input pin first, then let the tool follow the MMCM automatically for generated clocks; overriding the generated clock manually by name invites chasing ghosts. For an input clock whose edges are not at 0/50%, the waveform list states the rise and fall times explicitly.
3.2 Generated and divided clocks
Clock dividers written as RTL logic (a counter bit used as a clock) are an anti-pattern: the clock is derived from logic, its skew is uncontrolled, and STA treats it as data unless you declare:
create_generated_clock -name div2 -source [get_pins .../CLK] -divide_by 2 [get_pins reg/ Q]
The preferred design style is a single high-frequency clock with a clock-enable (CE) for low-rate logic — the CE keeps the design synchronous, halving the number of clock domains and eliminating most asymmetric-skew problems.
3.3 Clock groups and asynchronous clocks
Two clocks with no known phase relationship must be declared asynchronous, otherwise the tools assume the worst-case alignment and report (or try to fix) phantom paths:
set_clock_groups -asynchronous -group [get_clocks clk_a] -group [get_clocks clk_b]
If clock A and clock B relate but have different periods (e.g. 100 MHz and 33.333 MHz), define them as synchronous and the tools will compute all overlapping edges; if they genuinely come from separate oscillators, asynchronous treatment is compulsory.
4. Clock-Domain Crossing (CDC) Discipline
Even with correct clock-group declaration, physically crossing an asynchronous boundary with a register chain or a datapath is where metastability, data incoherency and intermittent failures are born. The safe hand-offs are: (1) a two- or three-flop synchronizer for single-bit control and flag signals; (2) a handshake (request/acknowledge) or a pulse-matching wrapper for control buses; (3) an asynchronous FIFO with binary/Gray-coded pointers for data streams. Every CDC path must be explicitly marked so the tools neither report it as a false path nor asynchronously optimize it:
set_false_path -from [get_cells sync_0/ff0/C] -to [get_cells sync_0/ff1/D]
A common trap is a “synchronized” multi-bit bus crossing with independent flops: the bits do not arrive together and the receiver can capture a corrupted mix. Always qualify multi-bit transfers with a synchronized ‘valid’/handshake pulse, or funnel the data through an async FIFO so that the write and read clocks never sample half-transferred words.
5. I/O Timing — Memory and Bus Interfaces
External interfaces add the board’s propagation delay as an unknown. For an input interface you must tell the tool the maximum external delay of the driving device (max input delay) and the minimum (min input delay) so the setup and hold checks at the FPGA capture flop are correct. For outputs you declare output delays referencing the capture device requirements. A DDR or SDR SDRAM controller interface is a classic case: the external clock must be forwarded (DQS/DQ skew), and the read/write timing is derived from the device datasheet numbers. The rule that prevents most head-scratching is to compute I/O delays from the external device’s own output-valid window and the PCB trace delay, rather than guessing a “safe” number that later fails on real boards.
6. Worked Example — Clocking a 32-bit AXI-Lite Slave at 100 MHz
Target: a 32-bit AXI-Lite peripheral registered via two flops, worst-case combinational depth 8 levels on the address decode, driven from a 100 MHz clock.
- Check the datasheet: flop Tcko = 0.9 ns, combinational stage ~0.3 ns, setup = 0.25 ns, clock skew budget 0.3 ns.
- Path path estimate: 0.9 + 8·0.3 + 0.25 + 0.3 = 3.85 ns « 10 ns — comfortably closed; the bottleneck is nowhere near the cycle.
- Now add the real-life stress: the fitter places the datapath across the device, routing adds 2–4 ns, and global clock skew approaches 1.5 ns at the far corner. The fully routed worst case lands ~8 ns, leaving ~2 ns slack — implementable but on the boundary.
- Constrain: create_clock 10 ns on the input pin; if a PLL is used, constrain the input pin and let the tool derive the generated clock.
- Verify: after implementation, read WNS — if WNS < 0, pipeline the decode (add a stage) or reduce the logic levels rather than tuning the constraint to hide the problem.
When the target changes to a higher frequency, the same cycle math applies; the switching frequency calculator on this site converts period↔frequency instantly, which is handy for sanity-checking the requested clock period before you commit it to the constraint file.
7. Common Timing Mistakes
- Constraining the PLL output instead of the input: input phase and jitter are then unknown; tools report suspect slack.
- Leaving unconstrained paths: an unconstrained domain is optimistically skipped; “clean” reports hide half the design.
- False-path abuse: applying set_false_path to a path that is actually functional hides real setup violations until the hardware misbehaves.
- Racing hold-time through optimization: hold failures appear late because routing shortens delays; always run hold analysis and fix with intentional delay or extra flops, not with random buffers.
- Multi-bit CDC without handshake: bits arrive at different times; the receiver sees a transiently corrupted word.
- Ignoring clock uncertainty: leaving jitter/uncertainty at 0 produces timing that passes in the tool but fails on silicon.
8. FAQ
Q: What is the difference between setup and hold violations? A: A setup violation means data arrives too late for the capture edge (frequency/too much logic); a hold violation means data changes too early after the edge (clock skew/routing, frequency-independent and invisible to simulation).
Q: Do I need to constrain every clock in a small design? A: Yes — every clock the flip-flops actually use must be declared with a real period; an undeclared clock leaves its paths unconstrained and the report meaningless.
Q: Is a “clean” timing report proof the design works? A: No — it is proof the constrained, annotated paths meet timing. Un-simulated CDC behavior, combinatorial hazards and unconstrained paths can still fail on hardware, especially with asynchronous boundaries.
Q: How do I fix a setup violation? A: Pipeline the critical path (add a register stage), reduce logic levels, enable retiming/register balancing, or relax the false/multi-cycle path definitions only where genuinely valid.
9. Conclusion
Timing constraints are not paperwork — they are the executable description of your design’s operating boundary. Declare clocks truthfully, separate asynchronous domains, mark every CDC with a safe synchronizer, annotate the I/O delays from datasheets, and drive closure with real analysis rather than constraint gymnastics. With those habits the FPGA will meet timing on the board, not just in the report.