GLS · Chapter 3 · Timing Basics for GLS
Setup, Hold & Timing-Check Fundamentals
A flip-flop captures data on a clock edge, but only if the data is stable around that edge. Setup is the window before the edge in which data must already be steady, and hold is the window after it in which data must stay steady. Miss either and the capture is unreliable. In gate-level simulation the cell model watches these windows with timing checks for setup, hold, width, recovery, and removal, and in most production libraries a violation toggles a notifier that drives the flop output to unknown X. This lesson makes the windows concrete, then draws the crucial line. Gate-level timing checks are dynamic and stimulus-limited, while static timing analysis checks every path exhaustively. STA is the real signoff; GLS is the dynamic complement.
Foundation12 min readGLSSetupHoldTiming ChecksSTA
Chapter 3 · Section 3.2 · Timing Basics for GLS
Project thread — 3.1 classified the counter's cell delays (pin-to-pin arcs). This lesson adds the timing checks on the counter's flops that guard capture; 3.3 adds interconnect delay to the picture.
1. Why Should I Learn This?
Setup and hold are the two rules every synchronous design lives by. Understanding them lets you:
- Read a flop
Xas a timing-check firing, not noise. - Know when GLS can flag a timing problem (and when it can't) — see why GLS exists.
- Keep GLS and STA in their correct lanes — dynamic vs signoff.
This is the timing vocabulary the rest of the chapter — interconnect delay, timing corners — and Chapter 4 (SDF) build on.
2. Real Silicon Story — the "passing" timing that STA never blessed
A gate-level run with SDF passed with no timing-check violations, and the team read it as timing closed.
STA — run separately — flagged a setup violation on a path the testbench never exercised. The GLS "pass" only meant no stimulus had violated a check; it said nothing about the unexercised path. STA, which checks all paths regardless of stimulus, caught it.
Lesson: absence of a GLS timing-check violation is not timing closure. GLS checks are dynamic and stimulus-limited; STA is the exhaustive signoff.
3. Concept — the windows and the checks
The windows (around a rising clock edge):
- Setup time — data must be stable for
t_subefore the edge. - Hold time — data must be stable for
t_hafter the edge. - Violate either → the captured value is unreliable (metastable in silicon;
Xin sim).
The checks (system tasks in the cell's specify block):
$setup (data, posedge clk, limit, notifier)— data steady before the edge.$hold (posedge clk, data, limit, notifier)— data steady after the edge.$width/$period— minimum pulse width / clock period.$recovery/$removal— async set/reset released a safe distance from the edge.
How a violation shows up: the check fires → toggles the notifier → the cell's UDP emits X on the output (2.5). A dynamic effect.
Scope: a check fires only if the stimulus violates it on that edge. STA checks every path statically — GLS does not prove all timing (0.3). The checks themselves live in the cell's specify block — see specify blocks and timing arcs.
4. Mental Model — a quiet zone around the clock edge
Picture a quiet zone bracketing the clock edge: setup before, hold after. Data may change outside the zone freely; inside it must stay still.
- Change inside the zone → the flop can't tell old from new → unreliable capture.
- The timing check is a guard watching the quiet zone; if data moves inside it, the guard raises the alarm — a message, and in a typical library a notifier toggle that drives
QtoX. - But the guard only watches when data actually moves on a real edge — it does not survey every possible path. That surveying is STA's job.
5. Working Example — a flop with checks, and a hold violation waveform
A representative flop model with checks and a notifier:
// Rising-edge DFF with timing checks + notifier — REPRESENTATIVE, tool-neutral
module DFFX1 (Q, D, CK);
output Q; input D, CK; reg notify;
DFF_udp_n u0 (Q, D, CK, notify); // sequential UDP (2.3), notifier wired in
specify
// Placeholder values. In a real flow SDF back-annotation overwrites every
// number here, which is why library models ship with nominal placeholders
// rather than hard-coded silicon timing.
(CK => Q) = 0.24; // clk-to-Q arc (-> SDF, 3.1/Ch4)
// NOTE the non-zero limits. A limit of 0 makes the check unfireable: no
// transition can ever fall inside a zero-width window, so a model written
// with `$setup(D, posedge CK, 0, notify)` silently never reports anything.
// Seeing zero limits in a model you are debugging is itself a finding.
$setup (D, posedge CK, 0.10, notify); // D steady 100ps BEFORE CK
$hold (posedge CK, D, 0.05, notify); // D steady 50ps AFTER CK
$width (posedge CK, 0.20, 0, notify); // min high pulse 200ps
endspecify
endmoduleA representative STA timing report — the static view of the same path:
# STA setup report — REPRESENTATIVE, tool-neutral (this is STATIC, path-based)
Startpoint: u_cnt/q_reg[1] (rising, CK)
Endpoint: u_cnt/q_reg[2] (rising, CK)
data arrival time 1.84
data required time 1.90 (clock period - setup - uncertainty)
----------------------------------
slack (MET) 0.06
# STA checks EVERY such path. GLS only sees paths the stimulus exercises.Slack — the number the report is actually reporting
The report above prints three numbers and a verdict, and the verdict is just subtraction. Since slack is the whole vocabulary of timing closure, it is worth deriving rather than reading.
Setup. Data launched by one edge must arrive before the next edge, early enough to satisfy the capture flop's setup window:
data arrival = launch clock latency + Tcq + logic delay
= 0.30 + 0.24 + 1.30 = 1.84 ns
data required = capture clock latency + Tperiod - Tsetup - uncertainty
= 0.30 + 2.00 - 0.10 - 0.30 = 1.90 ns
setup slack = required - arrival
= 1.90 - 1.84 = +0.06 ns METHold. Data launched by one edge must not arrive so early that it corrupts what the same edge is capturing:
data arrival = launch clock latency + Tcq(min) + logic(min)
= 0.30 + 0.12 + 0.09 = 0.51 ns
data required = capture clock latency + Thold
= 0.30 + 0.05 = 0.35 ns
hold slack = arrival - required
= 0.51 - 0.35 = +0.16 ns METTwo things in that pair carry most of the practical weight.
The subtraction runs the other way. Setup slack is required − arrival (data must be early enough); hold slack is arrival − required (data must be late enough). Both are "slack ≥ 0 passes," but they are opposite inequalities on the same path, which is why one path can pass setup and fail hold simultaneously.
Hold has no Tperiod term. Look at the two derivations: the period appears in the setup equation and is simply absent from the hold equation. That single structural fact explains the behaviour every engineer eventually meets —
A failing hold, so the sign is unambiguous. Take the same path but let the capture clock arrive 0.25 ns late relative to the launch clock — the classic skew direction that kills hold:
data arrival = 0.30 + 0.12 + 0.09 = 0.51 ns
data required = (0.30 + 0.25) + 0.05 = 0.60 ns
hold slack = 0.51 - 0.60 = -0.09 ns VIOLATEDThe data arrived 90 ps too early for a capture edge that came late — so the capture flop sees the next value instead of the one it was supposed to latch. In GLS, this is the path whose $hold check fires; in STA it is a negative number in a report, found whether or not any stimulus ever exercised it. Deeper treatment of each failure mode lives in setup violation debug and hold violation debug; reading the reports themselves is reading timing violations.
Practical context (representative, tool-neutral):
# STA and GLS are SEPARATE steps on the same netlist:
netlist/counter4.vg + lib(corner).lib + constraints.sdc --STA--> timing_report.rpt (+ SDF)
netlist/counter4.vg + lib/cells.v + sdf/counter4.sdf --GLS--> sim.log (+ waveform)
# STA = exhaustive static proof. GLS = dynamic functional confirmation with real delays.Here is a hold violation as a real waveform:
Hold violation: D changes too soon after the clock edge → Q goes X
8 cyclesRun it — a hold check firing, and not firing
The waveform above is a drawing. This is the same thing as a file you can compile, and it is worth running once because it makes the notifier convention from §3 visible: the X appears only because this UDP is written to produce one.
// ─────────────────────────────────────────────────────────────────────────────
// Drives one flop two ways: D moving well clear of the capture edge, then D
// moving INSIDE the hold window. The $hold check fires only in the second
// phase, and the notifier-driven UDP turns that into X on Q.
//
// iverilog -o hold_demo hold_check_demo.v && ./hold_demo
// vcs hold_check_demo.v && ./simv | xrun hold_check_demo.v
// (Run WITHOUT +notimingchecks — that switch disables the very check this
// demonstrates, which is itself worth confirming once.)
// ─────────────────────────────────────────────────────────────────────────────
`timescale 1ns/1ps
// Sequential UDP with a notifier input. The LAST table row is the entire
// X-on-violation convention: any change on `notifier` forces the output to x.
// Delete that row and the checks still fire and still print — Q simply stays
// clean. That is the point made in §3.
primitive udp_dff_n (q, ck, d, notifier);
output q; reg q;
input ck, d, notifier;
table
// ck d notifier : q : q+
(01) 0 ? : ? : 0 ;
(01) 1 ? : ? : 1 ;
(0?) 1 ? : 1 : 1 ;
(0?) 0 ? : 0 : 0 ;
(?0) ? ? : ? : - ;
(1x) ? ? : ? : - ;
? (??) ? : ? : - ;
? ? * : ? : x ; // notifier toggled -> corrupt to X
endtable
endprimitive
module DFFX1 (output Q, input D, CK);
reg notify;
udp_dff_n u0 (Q, CK, D, notify);
specify
(CK => Q) = 0.24;
$setup (D, posedge CK, 0.10, notify); // 100 ps setup window
$hold (posedge CK, D, 0.05, notify); // 50 ps hold window
endspecify
endmodule
module tb;
reg CK = 1'b0, D = 1'b0;
wire Q;
integer x_safe = 0, x_violate = 0;
reg in_violate_phase = 1'b0;
DFFX1 dut (.Q(Q), .D(D), .CK(CK));
always #1 CK = ~CK; // 2 ns period
always @(Q)
if (Q === 1'bx) begin
if (in_violate_phase) x_violate = x_violate + 1;
else x_safe = x_safe + 1;
end
initial begin
// ── Phase A: move D on the NEGEDGE — 1 ns clear of every capture edge ──
repeat (4) begin
@(negedge CK) D = ~D;
@(posedge CK);
end
#1;
// ── Phase B: move D 20 ps AFTER the capture edge — inside the 50 ps
// hold window, and far from the next edge so setup stays clean. This
// isolates $hold as the only check that can fire.
in_violate_phase = 1'b1;
repeat (4) begin
@(posedge CK);
#0.020 D = ~D;
end
#2;
$display(" X on Q, safe phase = %0d (expect 0)", x_safe);
$display(" X on Q, violating phase = %0d (expect > 0)", x_violate);
if (x_safe == 0 && x_violate > 0)
$display("\n [PASS] $hold fired only when D moved inside the hold window");
else
$display("\n [FAIL] unexpected notifier behaviour - check the UDP wiring");
$finish;
end
endmoduleExpected output. The exact violation text is tool-specific; the counts are not:
** Timing violation in tb.dut
$hold( posedge CK:3 ns, D:3.02 ns, 0.05 ns );
** Timing violation in tb.dut
$hold( posedge CK:5 ns, D:5.02 ns, 0.05 ns );
...
X on Q, safe phase = 0 (expect 0)
X on Q, violating phase = 4 (expect > 0)
[PASS] $hold fired only when D moved inside the hold windowThree things worth doing to this file, each of which teaches something the prose can only assert. Re-run with +notimingchecks (or your tool's equivalent) and the violations vanish while the design still "works" — that is why a clean GLS log proves nothing about the options it ran under. Delete the final UDP table row and the checks still fire and still print, but Q stays clean — that is the §3 point, executable. And move the #0.020 out past 0.05 and the violations stop, which locates the window boundary precisely where the $hold limit says it is.
6. Debugging Session — a clean GLS read as timing closure
A GLS run with SDF shows no timing-check violations and is declared timing-closed — but GLS only exercises stimulated paths, so a real setup violation on an unexercised path (which STA catches) was missed
NO GLS VIOLATION != TIMING CLOSURE (STA IS SIGNOFF)An SDF-annotated GLS run reports no $setup/$hold violations. The team concludes timing is closed. STA, run separately, flags a setup violation.
Gate-level timing checks are dynamic and stimulus-limited: a check fires only if the applied stimulus violates it on a given edge. Paths the testbench never exercises produce no violation — not because they are safe, but because they were never stressed. The violated path simply was not toggled by this stimulus. STA, which analyses every path statically against setup/hold, found it because it does not depend on stimulus.
Use STA for timing closure (all corners, 3.4) — that is the signoff. Use GLS timing checks as a dynamic complement: they confirm the design functions with real delays and flag violations your stimulus does exercise (often timing-dependent functional bugs). Never read "no GLS violation" as "timing proven." Its absence proves nothing; STA's met slack does.
7. Common Mistakes
- Reading "no GLS violation" as timing closure. Absence proves nothing — STA is signoff (0.3).
- Confusing setup and hold. Setup = before the edge; hold = after.
- Leaving checks active during reset / async crossings. Common false-fire sources — triage in real vs false violations.
- Treating a notifier-
Xas a random glitch. It is a timing-check firing — diagnosable via timing checks and notifiers, and traced with where X comes from. - Using the wrong corner for the check limits / SDF — timing corners for GLS.
8. Industry Best Practices
- STA closes timing; GLS confirms function with real delays — keep the lanes distinct.
- Recognise notifier-
Xby its$setup/$holdmessage and triage real vs false violations. - Match check limits / SDF to the signoff corner — min/typ/max selection.
- Setup vs hold intuition: setup fails when logic is too slow; hold fails when a path is too fast/short.
- Do not gate reset-window / async-crossing checks without justification — see controlling X propagation.
Senior Engineer Thinking
- Beginner: "GLS had no timing violations, so timing is done."
- Senior: "GLS only tested the paths my stimulus drove. Timing closure is STA, across all corners. GLS confirms the design works with real delays and flags violations I happened to exercise."
The senior treats STA as signoff and GLS timing checks as a dynamic, stimulus-bounded complement.
Silicon Impact
Read "no GLS violation" as closure and you may skip or under-weight STA — letting a real setup/hold violation on an unexercised path reach silicon as an intermittent, corner-sensitive failure. Conversely, chasing every notifier-X as a real bug wastes effort on artifacts (reset-window checks, async crossings). The discipline: STA proves timing across all paths and corners; GLS timing checks dynamically confirm function and surface exercised violations. That split keeps timing bugs off the tape-out.
Engineering Checklist
- Used STA (all corners) for timing closure — not GLS.
- Treated GLS timing checks as a dynamic complement.
- Recognised notifier-
Xand triaged real-vs-artifact. - Matched check limits / SDF to the signoff corner (3.4).
- Did not read "no GLS violation" as timing proven.
Try Yourself
- Instantiate the representative
DFFX1and clock it withDchanging well clear of the edge —Qis clean. - Observe: now move a
Dtransition into the hold window afterposedge CK; the$holdcheck fires andQgoesX. - Change: move
Dearlier so it is stable through the window; re-run. - Expect: no violation, clean
Q. Then note: you only tested the edges you drove — STA would check every path. Prove the scope to yourself.
Any free Verilog simulator supports specify timing checks and notifiers. Real check limits come from the .lib/SDF (vendor/PDK). No paid tool required.
Interview Perspective
- Weak: "Setup and hold are just timing numbers."
- Good: "Setup is the stability window before the clock edge, hold is after; a violation drives the flop output to
Xvia the notifier." - Senior: "Setup guards logic being too slow, hold guards a path being too fast. GLS timing checks fire dynamically only on stimulated violations, so they don't prove timing — STA does, statically, across all paths and corners. GLS is the functional complement."
9. Interview / Review Questions
Where This Is Specified
Timing checks are language constructs, defined in IEEE Std 1364 (Verilog) clause 15 — Timing checks and carried into IEEE Std 1800 (SystemVerilog) clause 31, together with the specify block that hosts them. That is where $setup, $hold, $setuphold, $width, $period, $recovery, $removal and the notifier argument are defined — and, importantly, where the definition stops: the standard says a violated check may toggle its notifier, and says nothing about driving the output to X. That part is the cell model's, as §3 sets out. The IEEE Standards Association listing is the primary source.
The numbers those checks use are not in the language at all. Setup/hold limits and cell delays come from the vendor's Liberty (.lib) characterisation, reach simulation through SDF back-annotation (IEEE Std 1497), and reach STA through the timing library directly — which is why the placeholders in the model above are overwritten in any real flow. See what is SDF and delay types in gate simulation.
10. Key Takeaways
- Setup = data stable before the clock edge; hold = data stable after it. Violate either and capture is unreliable (metastable in silicon; typically
Xin sim). - Slack is subtraction, and it runs opposite ways: setup slack =
required − arrival; hold slack =arrival − required. Hold has no period term, which is why slowing the clock never fixes a hold violation. - Cell models watch the windows with timing checks —
$setup,$hold,$width,$recovery,$removal— and in a typical library a violation toggles the notifier, whose UDP then drives the output toX. TheXis the model's convention, not the language's (2.5). - Gate-level timing checks are dynamic and stimulus-limited — they fire only on violations the stimulus exercises.
- STA checks every path statically and exhaustively — it is the timing signoff; GLS is the dynamic complement (0.3).
- "No GLS violation" ≠ timing closed. Absence proves nothing; met STA slack (all corners, 3.4) does. Next: 3.3 — interconnect delay & the post-layout picture.
Quick Revision
Setup = before the edge; hold = after. Cell models watch them with
$setup/$hold/$width/$recovery/$removal; a violation → notifier →Q = X(dynamic). GLS checks are stimulus-limited; STA (static, exhaustive, all corners) is timing signoff. "No GLS violation" proves nothing. Next: 3.3 — interconnect delay.