SystemVerilog · Module 17
Specify Blocks & Path Delays
SystemVerilog specify blocks — model real pin-to-pin propagation delays and timing checks for gate-level simulation. Simple/full/edge-sensitive path forms, specparam vs parameter, the system timing checks ($setup, $hold, $setuphold, $width, $period, $recovery, $removal), SDF back-annotation, and the full GLS flow with min/typ/max corners.
Module 17 · Page 17.4
RTL simulation is zero-delay: assign y = a & b; makes y change the instant a or b does. Real silicon does not work that way — every gate has a pin-to-pin propagation delay, and every flip-flop has setup/hold requirements. The specify block is how a Verilog/SystemVerilog model carries that timing: it declares path delays (how long a transition takes to travel from an input pin to an output pin) and timing checks ($setup, $hold, …) that flag violations during simulation. Its values are named with specparam, which a Standard Delay Format (SDF) file from place-and-route can override at runtime — and that mechanism is the backbone of gate-level simulation (GLS), where the post-layout netlist runs with the chip's real delays. This lesson covers the three path-delay forms, specparam, the full timing-check set, and the SDF/GLS flow.
1. Engineering Problem — Zero-Delay RTL Hides Real Timing
Functionally-correct RTL can still fail on silicon because of timing. A combinational cone that is too deep does not meet the clock period; a data path that arrives too close to the clock edge violates setup; a fast path that arrives too early violates hold. None of this is visible in zero-delay RTL simulation, where every signal settles instantly.
You need a model that carries real delays so the simulator can show the truth: y changing 45 ps after its input, a flip-flop's Q arriving 340 ps after the clock edge, a register sampling stale data because setup was missed. That model is the specify block. Cell-library vendors ship it inside every standard-cell model (AND2X1, DFFX1, …); the place-and-route tool measures the actual post-layout delays and emits them as SDF; the simulator back-annotates the SDF onto the cells and runs gate-level simulation with the chip's true timing. Setup/hold violations then surface as X on the output — the same X that becomes a real functional failure on silicon.
2. Mental Model — Timing Layered Over Zero-Delay Function
The picture every engineer carries:
A
specifyblock adds a timing skin over a module's zero-delay function. The logic (and (Y,A,B)orassign) still computes what the output is; thespecifyblock declares when it gets there — a pin-to-pin path delay for each arc — plus timing checks that watch the relationship between signals and fire on a violation. Delay values are named withspecparam, and unlike aparameter, aspecparamcan be overridden at simulation runtime by an SDF file without recompiling. That single property is the whole reasonspecifyexists: it lets one netlist run with min, typical, or max post-layout delays selected at the command line.
Four invariants this picture preserves:
specifyis module-scope and simulation-only. It sits besidealways/assign, never inside them, and synthesis ignores it entirely — it is a timing model, not logic.- Paths are pin-to-pin (port-to-port). A path delay connects module ports, never internal wires.
specparamis the SDF hook. Timing constants must bespecparam(notparameter) for SDF back-annotation to reach them.- Timing checks need real delays to mean anything.
$setup/$holdon a zero-delay netlist never fire; they earn their keep in GLS with SDF-annotated delays, where the static counterpart is STA (static timing analysis).
3. Visual Explanation — Pin-to-Pin Path Delays
The functional and still decides Y's value; the specify block puts a measured delay on each input-to-output arc.
The payoff is concrete: in RTL, assign Y = A & B makes Y change the moment B does; with the specify arc (B => Y) = (45, 40), Y's rise lands 45 time units after B rises. GLS adds exactly this realism so paths that miss the target frequency become visible.
4. The Three Path-Delay Forms
4.1 Simple (parallel) path — in => out
Connects sources to destinations 1-to-1 by position. The delay value can be 1, 2, 3, or 6 numbers depending on how much transition detail you model.
specify
(in_a => out_y) = 5; // one value: rise = fall = z
(in_a => out_y) = (10, 8); // two: (rise, fall) — most common
(in_a => out_y) = (10, 8, 9); // three: (rise, fall, z-transition)
(in_a => out_y) = (10, 8, 9, 7, 9, 7); // six: (01,10,0z,z1,1z,z0)
(in_a, in_b => out_y, out_z) = 10; // paired 1:1 — a→y, b→z
endspecify4.2 Full path — in *> out
The *> operator connects every source to every destination (a cross-product) — one line instead of one per input when several inputs share a delay to the same output.
module mux4 (input logic a, b, c, d, sel0, sel1, output logic y);
assign y = sel1 ? (sel0 ? d : c) : (sel0 ? b : a);
specify
(a, b, c, d *> y) = 12; // all data inputs → y, same 12-unit delay
(sel0, sel1 *> y) = 15; // select inputs → y, slightly longer
endspecify
endmodule4.3 Edge-sensitive path — for flip-flops
A clock edge triggers the output transition. (posedge clk => (q : d)) = delay reads "on posedge clk, output q takes the value of d after delay" — exactly the clk-to-Q propagation of a flop.
module dff (input logic clk, rst_n, d, output logic q, qn);
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) q <= 1'b0;
else q <= d;
assign qn = ~q;
specify
specparam tCQR = 7, // clk-to-q rising
tCQF = 6, // clk-to-q falling
tPCQ = 8; // async clear-to-q
(posedge clk => (q : d)) = (tCQR, tCQF); // clk-to-Q
(posedge clk => (qn : d)) = (tCQF, tCQR); // qn is the complement
(negedge rst_n => (q : 1'b0)) = tPCQ; // async reset clears q
$setup(d, posedge clk, 5); // timing checks — §6
$hold (posedge clk, d, 3);
endspecify
endmodule5. specparam — Timing Parameters the SDF Can Override
specparam is the timing-specific cousin of parameter: it names delay values so you write tRISE instead of a bare 10. The decisive difference — specparam values can be overridden at runtime by an SDF file without recompiling, which is how post-layout timing is injected into GLS.
specify
specparam tRISE = 10, tFALL = 8, tSETUP = 5, tHOLD = 3;
(in_a => out_y) = (tRISE, tFALL); // named delays in a path
$setup(d, posedge clk, tSETUP); // named delays in a check
$hold (posedge clk, d, tHOLD);
endspecifyparameter | specparam | |
|---|---|---|
| Purpose | Design structure — widths, depths | Timing — delays, setup/hold limits |
| Overridden by | #( ) at instantiation | SDF file at runtime (no recompile) |
| Scope | Module | Inside (or at module scope for) specify |
6. System Timing Checks
System timing checks are special system tasks inside a specify block that enforce temporal relationships between signals and report a violation during simulation. The canonical pair is setup (data stable before the clock edge) and hold (data stable after it); a violation models metastability and drives the output to X.
| Task | Checks | Syntax |
|---|---|---|
$setup | data stable before the reference edge by ≥ limit | $setup(data_event, ref_event, limit) |
$hold | data stable after the reference edge for ≥ limit | $hold(ref_event, data_event, limit) |
$setuphold | both, in one statement | $setuphold(ref_event, data_event, t_su, t_hold) |
$width | pulse stays high/low ≥ limit | $width(event, limit) |
$period | clock period ≥ limit | $period(ref_event, limit) |
$recovery | async-reset release ≥ limit before next clock edge | $recovery(ref_event, data_event, limit) |
$removal | async reset held ≥ limit after clock edge | $removal(ref_event, data_event, limit) |
$skew | edge-to-edge skew ≤ limit | $skew(ref_event, data_event, limit) |
specify
specparam tSETUP = 5, tHOLD = 3, tWIDTH = 10, tPERIOD = 20, tRECOV = 4, tREMOV = 2;
$setup(d, posedge clk, tSETUP); // d stable tSETUP before posedge clk
$hold (posedge clk, d, tHOLD); // d stable tHOLD after posedge clk
$setuphold(posedge clk, d, tSETUP, tHOLD);
$width (posedge clk, tWIDTH); // clk-high pulse width
$period(posedge clk, tPERIOD); // clk period
$recovery(posedge rst_n, posedge clk, tRECOV); // async-reset recovery
$removal (posedge rst_n, posedge clk, tREMOV); // async-reset removal
endspecify$ vcs -v2k -sdf max:top:top.sdf top_netlist.v tb_top.v -o sim_gls && ./sim_gls
SDF annotation complete (2847 instances, 12341 arcs)
SETUP VIOLATION on d in dff (top.u_core.u_reg_A)
At time 4250ns Setup time: 5ns Actual: 3ns → output q set to X (metastability)
Total timing violations: 3 → PASS (functional) / FAIL (timing)7. SDF Back-Annotation — The Gate-Level Simulation Flow
SDF (Standard Delay Format) is a text file emitted by place-and-route containing the actual post-layout delays for every cell and interconnect. Back-annotation loads it into the simulator, overriding the specparam values in the cell models with real numbers.
The flow is three steps: (1) synthesis + place-and-route map RTL to cells, route wires, and compute real delays → gate-level netlist + SDF; (2) the simulator loads the SDF and netlist, overriding every cell's specparam at the named hierarchy; (3) GLS runs with real timing, and setup/hold violations appear as X on outputs.
(DELAYFILE
(SDFVERSION "3.0") (DESIGN "top") (TIMESCALE 1ps)
(CELL
(CELLTYPE "DFFX1")
(INSTANCE top.u_core.u_reg_A)
(DELAY (ABSOLUTE
(IOPATH CLK Q (312:340:370) (298:325:355))) // (min:typ:max) rise / fall
)
(TIMINGCHECK
(SETUP D (posedge CLK) (85:95:110)) // min:typ:max
(HOLD (posedge CLK) D (40:45:52)))
)
)# VCS — back-annotate with the max corner
vcs -v2k -negdelay -timescale=1ns/1ps -sdf max:top:top.sdf \
top_netlist.v prim_lib.v tb_top.v -o sim_gls
./sim_gls +notimingchecks # functional GLS — path delays only, no setup/hold
./sim_gls # full GLS — includes timing-check violations
# Questa
vsim -sdfmax /top=top.sdf top
# Xcelium
xrun -v2k -negdelay -sdf_file top.sdf top_netlist.v prim_lib.v tb_top.v8. Full Working Example — Standard-Cell AND Gate with Timing
The shape of a real standard-cell model: zero-delay function plus a specify block of specparams and path delays.
// AND2X1 — the kind of model a cell library (TSMC, GF, …) ships
module AND2X1 (input logic A, B, output logic Y);
and (Y, A, B); // gate primitive — the zero-delay function
specify
// Named delays — overridden by SDF at runtime (ps in a real library)
specparam A_Y_RISE = 42, A_Y_FALL = 38,
B_Y_RISE = 45, B_Y_FALL = 40;
(A => Y) = (A_Y_RISE, A_Y_FALL); // pin-to-pin arcs
(B => Y) = (B_Y_RISE, B_Y_FALL);
// No timing checks — $setup/$hold apply only to sequential cells
endspecify
endmodule
module tb_and;
logic a, b, y;
AND2X1 u_and (.A(a), .B(b), .Y(y));
initial begin
a = 0; b = 0;
#10 a = 1; // Y stays 0 (b still 0)
#10 b = 1; // at t=20 both are 1, but Y not yet risen
#50 $display("t=%0t A=%b B=%b Y=%b", $time, a, b, y);
end
endmodulet=0 A=0 B=0 Y=0
t=10 A=1 B=0 Y=0 ← Y stays 0 (b still 0)
t=20 A=1 B=1 Y=0 ← delay not elapsed yet
t=65 A=1 B=1 Y=1 ← Y rises at t=20+45 (B→Y rise delay = 45)9. Industry Usage — Where Specify Blocks Live
- Standard-cell libraries. Every cell model (
AND2X1,DFFX1,MUX2X1, …) from TSMC, GlobalFoundries, Samsung, etc. ships with aspecifyblock; thespecparams are placeholders the SDF overwrites. - Gate-level simulation (GLS) sign-off. Post-layout netlist + SDF is simulated to confirm the design still functions and meets timing after place-and-route — catching X-propagation and reset/clock issues that pure STA can miss.
- The STA relationship. Static timing analysis is the static counterpart — it checks every path's setup/hold against constraints without simulation.
specifytiming checks are the dynamic counterpart, firing on the specific vectors GLS exercises. Real flows use both: STA for exhaustive coverage, GLS for X/reset/async behaviour. - ASIC bring-up.
$recovery/$removalchecks on async reset release are a frequent source of real silicon bugs, and GLS is where they first show up.
Specify blocks are simulation-only — synthesis ignores them. They model timing; they never become gates.
10. Debugging Guide — The Mistakes Everyone Makes Once
specify blocks — bugs every engineer hits the first time
Symptom: Syntax error at the specify keyword.
Cause: specify is a module-scope item, parallel to always/assign — it cannot live inside an always/initial block.
always @(posedge clk) begin specify ... endspecify end // ❌ illegalFix: move specify … endspecify to module scope, between the ports and endmodule, alongside the always/assign statements.
Guardrail: a specify block is a sibling of always, never a child.
Symptom: SDF back-annotation runs but the delays never change — GLS uses the hard-coded values.
Cause: SDF can only override specparam. A parameter (or a literal) in a path delay is invisible to SDF annotation.
specify parameter tRISE = 10; (a => y) = tRISE; endspecify // ❌ SDF can't reach itFix: use specparam for every timing constant; reserve parameter for structural values (widths, depths).
Guardrail: if a number is a delay or a setup/hold limit, it is a specparam.
Symptom: GLS passes clean, yet silicon has setup/hold failures.
Cause: +notimingchecks skips all $setup/$hold evaluation — you get path delays but no violation detection, defeating the purpose of full GLS.
Fix: run at least the critical tests without +notimingchecks. If false X-propagation floods the log, fix it with SDF scoping or X-pessimism options — not by disabling all checks.
Guardrail: +notimingchecks is for early bring-up only; sign-off GLS runs checks on.
Symptom: Violation reports that point at the wrong signal or never fire.
Cause: $setup(data, ref, limit) lists data first; $hold(ref, data, limit) lists the reference first. Swapping them inverts the check.
Fix: remember setup = data arrives before clock (data first); hold = clock already fired (ref first). Or use $setuphold(ref, data, t_su, t_hold) to avoid the trap.
Guardrail: when in doubt, $setuphold — one consistent argument order for both.
Symptom: Compile error — the path's signal "is not a port."
Cause: Specify-block paths are pin-to-pin (port-to-port) only; an internal wire cannot be a path endpoint.
(internal_wire => out_y) = 5; // ❌ internal_wire is not a module portFix: model the composite delay as a single port-to-port arc, or (rarely) promote the internal signal to a port if the timing must be exposed.
Guardrail: path endpoints are always module ports — specify describes the cell's external timing, not its internals.
11. Q & A
12. Cross-References & What's Next
This lesson covered specify blocks — the three path-delay forms, specparam, the system timing checks, and the SDF/GLS flow.
- Previous: 17.3 — Checkers — assertion-based verification;
specifyis its timing-model counterpart for gate-level sign-off. - Module index: Module 17 — Advanced Topics — the advanced-feature arc.
Related material elsewhere in the curriculum:
- Input & Output Skews — clocking-block skews are the testbench-side timing-margin control;
specifytiming checks are the design-model side. - Clocking Blocks — Deep Dive — sampling/driving timing in the testbench, the dynamic-verification companion to gate-level timing.
13. Quick Reference
specify
// ── specparam (SDF-overridable timing constants) ──────────────────
specparam tRISE = 10, tFALL = 8, tSU = 5, tHD = 3, tW = 20;
// ── Simple (parallel) path — 1-to-1 ───────────────────────────────
(in_a => out_y) = 10; // single delay
(in_a => out_y) = (tRISE, tFALL); // (rise, fall)
(in_a => out_y) = (10, 8, 9); // (rise, fall, z)
// ── Full path — all-to-all ────────────────────────────────────────
(in_a, in_b, in_c *> out_y) = 12;
// ── Edge-sensitive path (flip-flop) ───────────────────────────────
(posedge clk => (q : d)) = (tRISE, tFALL); // clk-to-Q
(negedge rst_n => (q : 1'b0)) = tFALL; // async reset-to-Q
// ── Timing checks (mind $setup vs $hold arg order) ────────────────
$setup (d, posedge clk, tSU);
$hold (posedge clk, d, tHD);
$setuphold(posedge clk, d, tSU, tHD);
$width (posedge clk, tW);
$period (posedge clk, 20);
$recovery (posedge rst_n, posedge clk, 4);
$removal (posedge rst_n, posedge clk, 2);
endspecify
// GLS: vcs -sdf max:top:top.sdf ... (max for setup, min for hold)
// specify is module-scope + simulation-only; synthesis ignores it.14. Summary
A specify block layers real timing over a module's zero-delay function: path delays declare how long each pin-to-pin arc takes, and timing checks ($setup, $hold, $setuphold, $width, $period, $recovery, $removal, $skew) flag temporal violations. Paths come in three forms — simple => (1-to-1), full *> (all-to-all), and edge-sensitive (posedge clk => (q : d)) for flops — with 1/2/3/6-value delay specifications.
Timing constants must be specparam, not parameter, because only specparam can be overridden by an SDF file at runtime — the mechanism that injects post-layout delays into gate-level simulation without recompiling. Run GLS at both corners (max for setup, min for hold), keep timing checks on for sign-off, and mind the mirror-image argument order of $setup and $hold. specify is module-scope and simulation-only — synthesis ignores it — and it is the dynamic complement to STA: STA proves every path statically, while specify/SDF/GLS exposes the X-propagation, reset, and async behaviour that only a real-delay simulation reveals.
Continue learning
Related tutorials
- Related topic
Input & Output Skews
SystemVerilog clocking block skews — the granular timing margins around each clock edge. Input skew controls exactly when the testbench samples DUT outputs; output skew controls when the testbench drives DUT inputs. #1step, #N, #0, and #-N — what each resolves to, when each is right, and the protocol-specific choices for APB / AXI / SPI / DDR.
- Related topic
Clocking Blocks — Deep Dive, SVA, Coverage, Multi-Clock SoC, CDC
SystemVerilog clocking blocks at production depth — input/output skews, the ##N operator, multi-clocking-block patterns, default clocking, and the deeper architectural role as the foundation of SVA, functional coverage, multi-clock SoC verification, and clock-domain crossing (CDC) discipline.
Standards & specifications
- Governing standard
- IEEE Std 1800 (SystemVerilog)(opens IEEE in a new tab)
Defines SystemVerilog language semantics — syntax, data types, scheduling and the behaviour a conforming simulator must produce. It does not define tool-specific synthesis support or vendor methodology.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the SystemVerilog curriculum.
