Timing formulae and rules of thumb
Setup and hold inequalities, fmax from slack, synchroniser MTBF, I/O timing budgets, DDR window arithmetic, pipelining gain and fixed-point bit growth, all in plain text.
This page collects the arithmetic behind timing reports and interface budgets. It is for engineers who want to check a tool’s number by hand or size a design before the tool exists. Symbols are defined once at the top and reused. Every formula is written in plain text so it can be pasted into a calculator or a script.
Symbols
| Symbol | Meaning | Typical value, mid-range FPGA, 28 nm to 16 nm |
|---|---|---|
| T | clock period | 5 to 10 ns |
| t_cko | clock-to-output of the launching flop | 0.2 to 0.5 ns |
| t_logic | combinational delay including routing | depends on design |
| t_su, t_h | setup and hold requirement at the capturing flop | 0.05 to 0.2 ns each |
| t_skew | capture clock arrival minus launch clock arrival | plus or minus 0.1 to 0.3 ns on a global net |
| t_j | peak-to-peak clock jitter, or the tool’s uncertainty | 0.05 to 0.2 ns |
| WNS, TNS | worst negative slack, total negative slack | from the report |
Setup and hold inequalities
Setup: t_cko + t_logic(max) + t_su + t_j <= T + t_skew
Hold: t_cko + t_logic(min) >= t_h + t_skew + t_j
Positive skew (capture clock arrives late) helps setup and hurts hold. Hold does not depend on the period, which is why a hold violation cannot be fixed by lowering the clock frequency. The tool fixes hold by adding routing delay, and reports it as a separate slack.
Setup slack = T + t_skew - (t_cko + t_logic(max) + t_su + t_j)
Hold slack = t_cko + t_logic(min) - (t_h + t_skew + t_j)
fmax from the report
fmax = 1 / (T - WNS) # WNS is negative when failing
Example: a 10 ns constraint with WNS of minus 0.8 ns gives fmax of 1 / 10.8 ns, which is 92.6 MHz. With WNS of plus 1.2 ns the design could run at 1 / 8.8 ns, which is 113.6 MHz, but only if every other path has at least that much slack, which the report does not promise. TNS tells you how far away you are overall: a large TNS with small WNS means many paths need work, not one.
| Rule of thumb | Value |
|---|---|
| Logic levels per 100 MHz on a 28 nm fabric | about 8 to 10 LUTs between flops |
| Logic levels at 300 MHz on the same fabric | 2 to 3 |
| Fraction of the period spent on routing in a congested design | 50 to 70 % |
| Fan-out above which a net starts to cost a logic level | about 32 (varies widely) |
Synchroniser MTBF
For a two-flop synchroniser, the mean time between metastability failures is usually modelled as
MTBF = exp(t_r / tau) / (T_0 * f_clk * f_data)
where t_r is the time available for the first flop to resolve (one capture period minus the second flop’s setup and the routing between them), tau is the flop’s resolution time constant, T_0 is a window constant that depends on the flop, f_clk is the capture clock frequency and f_data is the rate at which the input changes.
Worked example (illustrative constants, not any vendor’s numbers):
tau = 20 ps
T_0 = 50 ps
f_clk = 200 MHz, so T = 5 ns
f_data = 50 MHz
t_r = 5 ns - 0.3 ns (setup plus routing) = 4.7 ns
exp(4.7 ns / 20 ps) = exp(235) = about 1.15e102
denominator = 50e-12 * 200e6 * 50e6 = 5e5
MTBF = about 2.3e96 seconds
The same arithmetic with one flop only (t_r near zero) gives an MTBF measured in seconds, which is why one flop is never enough. Vendors publish tau and T_0 per family. AMD exposes them through report_synchronizer_mtbf in Vivado when ASYNC_REG is set. Adding a third flop multiplies the exponent’s numerator by nearly another period, which is why three flops are common above 400 MHz.
I/O timing budgets
System-synchronous (shared clock from the board)
Input setup check: t_clk_to_fpga + t_co(ext) + t_pcb(data) + t_su(fpga) <= T + t_pcb(clk)
Input hold check: t_co(ext,min) + t_pcb(data,min) >= t_h(fpga) + t_pcb(clk) - t_pcb(clk,min)
What you write in the constraint file, where the external device’s datasheet gives t_co(max) and t_co(min) and the board gives trace delays:
set_input_delay -max = t_co(ext,max) + t_pcb(data,max) - t_pcb(clk,min)
set_input_delay -min = t_co(ext,min) + t_pcb(data,min) - t_pcb(clk,max)
set_output_delay -max = t_su(ext) + t_pcb(data,max) - t_pcb(clk,min)
set_output_delay -min = -t_h(ext) + t_pcb(data,min) - t_pcb(clk,max)
The output -min value is usually negative. Board traces run at roughly 6 to 7 ps per mm on FR-4 microstrip, so a 50 mm mismatch is about 0.3 ns.
Source-synchronous (clock forwarded with the data)
The external clock and data travel together, so the board delays cancel and only the skew between them matters.
Data valid window at the FPGA pin = T_bit - t_skew(ext) - t_pcb_mismatch - t_jitter
Required window = t_su(fpga) + t_h(fpga) # from the datasheet, per I/O standard
Margin = valid window - required window
Centre the capture edge in the window using the input delay element (IDELAY on AMD, dynamic phase shift on Altera, DLL on Lattice) or by shifting the PLL phase. Edge-aligned interfaces need a 90 degree shift, centre-aligned ones need none.
DDR data-valid window
For double data rate, a bit lasts half a period.
T_bit = T / 2
Window = T_bit - t_dvw_loss
t_dvw_loss = t_skew(driver) + t_pcb_mismatch + t_duty_error + t_jitter
Example at 800 Mb/s per pin (400 MHz DDR, T = 2.5 ns):
T_bit = 1.25 ns
driver skew = 0.20 ns
PCB mismatch = 0.05 ns (within 8 mm)
duty error = 0.10 ns (4 %)
jitter = 0.10 ns
window = 1.25 - 0.45 = 0.80 ns
An FPGA input with t_su + t_h of 0.40 ns leaves 0.20 ns each side once centred, which is workable but leaves no room for temperature drift without calibration. Memory controllers calibrate for exactly this reason.
Pipelining gain estimate
Splitting a combinational block of delay D into N balanced stages:
T_new = D / N + t_cko + t_su + t_overhead
Speedup = (D + t_cko + t_su) / T_new
Latency = N cycles
The overhead term (register delay plus the routing that does not shrink) is typically 0.5 to 0.8 ns on a 28 nm fabric, which caps the useful N. Going from 1 to 2 stages roughly doubles fmax. Going from 4 to 8 rarely gives more than 20 % because the overhead dominates. Pipelining does not help if the block feeds back to itself within the loop: for an accumulator, retiming across the loop is impossible and you need a different algorithm.
Fixed-point bit growth
Widths in bits, with signed two’s complement unless stated.
| Operation | Result width | Note |
|---|---|---|
| a + b, both N bits | N + 1 | one carry bit |
| Sum of M terms of N bits | N + ceil(log2(M)) | an accumulator of 1024 samples adds 10 bits |
| a × b, N and M bits unsigned | N + M | |
| a × b, N and M bits signed | N + M - 1 | except for the one case of minimum times minimum, which needs N + M; most tools keep N + M |
| Multiply by a constant of K bits | N + K | |
| FIR with M taps of C-bit coefficients and N-bit data | N + C + ceil(log2(M)) | before any rounding |
| Shift left by s | N + s | |
| Truncation of F fraction bits | N - F | introduces a bias of minus half an LSB; round-half-even or add 1 at the dropped bit to centre it |
Keep the full width through the arithmetic and round once at the end. Rounding at every stage is cheaper in flops but stacks bias and can cause limit cycles in IIR filters.
Sources
- UG949: UltraFast Design Methodology Guide, timing closure chapter
- UG906: Vivado Design Analysis and Closure Techniques
- Quartus Prime Pro Edition User Guide: Timing Analyzer (document 683243)
- C. Cummings, “Clock Domain Crossing (CDC) Design and Verification Techniques Using SystemVerilog”, SNUG 2008
- Related on this site: /reference/xdc-sdc-constraints/ and /reference/reset-and-cdc-patterns/