GLS · Chapter 14 · Industry Case Studies
Case Study — Counter: Full-Timing GLS with SDF
The second case study adds timing to the flow. It takes the counter, the project thread's next design after the flip-flop, and runs it as a full-timing gate-level simulation with SDF. It ships the full artifact set: the RTL in three languages, the synthesized netlist, an SDF with cell and interconnect delays and setup and hold checks, an adapted testbench, an expected log, and a debug walkthrough. Beyond the flip-flop's functional clean run, this case verifies SDF annotation with zero unmatched instances, shows real clock-to-Q delays and firing timing checks, and selects the right corner and value. It defines full-timing clean as verified annotation plus no real violations plus no unexplained X, and it holds the line that full-timing GLS is a dynamic spot-check, not the exhaustive timing signoff that static timing analysis provides.
Foundation13 min readGLSCase StudyCounterSDFFull-Timing
Chapter 14 · Section 14.2 · Industry Case Studies
Project thread — the DFF (14.1) becomes a counter with SDF timing here. 14.3 does X/reset signoff on the FSM; 14.4–14.6 scale to peripherals and the mini-SoC.
1. Why Should I Learn This?
This is the timing half of a clean-GLS signoff — the DFF gave function, the counter adds real delays.
- Verify SDF annotation (0 unmatched) — or it's silently zero-delay (4.5).
- Real clk-to-Q + timing checks — the timed signature (3.1/3.2).
- Full-timing clean = verified SDF + no real violations + no unexplained
X— and it's a spot-check, not STA.
This is the template for every timed case that follows.
2. Real Silicon Story — the "timed" counter that was zero-delay
A team signed off the counter's full-timing GLS — but the run was silently zero-delay (no SDF actually annotated, 4.5), so it had verified nothing about timing.
The $sdf_annotate had failed a scope match and no one read the coverage log. The counter's transitions were instant (no clk-to-Q), and the "timing" signoff was meaningless. Verifying annotation (0 unmatched) surfaced the failure; once fixed, the counter showed real clk-to-Q delays and the timing checks actually ran.
Lesson: a full-timing run is only timed if the SDF is verified-annotated (0 unmatched, 4.5) — always read the coverage log. A silently zero-delay run signs off nothing about timing.
3. Concept — the counter full-timing case
The design: the 4-bit counter (state flops + increment logic) — real state and datapath.
Adding timing (Chapters 3–4):
- SDF carries cell delays (
IOPATH), net delays (INTERCONNECT), and timing checks (SETUP/HOLD), eachMIN:TYP:MAX. $sdf_annotateat time 0, correct scope; verify coverage (0 unmatched, 4.5).- Corner (3.4): use the intended corner (setup→slow, hold→fast);
MIN:TYP:MAXselection (4.4).
The timed signature:
- Power-up
X→ reset0→ count0,1,2,3,…(as in 14.1's flop, now a counter) — but each count transition lands a real clk-to-Q delay after the clock edge (not instantly). - Timing checks ($setup/$hold) run on the flops (2.5/3.2).
Full-timing clean (the bar):
- SDF annotation verified (0 unmatched, 4.5) — the run is actually timed.
- No real timing violations — firings triaged real-vs-artifact (8.4); artifacts fixed in the run, real ones are design issues.
- No unexplained
X(14.1's bar still holds).
Scope (accuracy):
- Full-timing GLS is a dynamic spot-check (exercised paths, this stimulus) — not the exhaustive timing signoff; STA is that (0.3). GLS stays dynamic.
4. Mental Model — the DFF with a stopwatch and a datapath
The counter full-timing case is the DFF case (14.1) plus two things: a stopwatch (SDF timing) and a datapath (the increment ring).
- The functional story is the same — power-up
X, reset, then it runs (counts). - The stopwatch (SDF) makes every transition happen a real clk-to-Q delay after the edge — but only if the stopwatch is actually running (SDF verified-annotated, 4.5; a stopped stopwatch = silent zero-delay).
- The timing checks watch the flops' setup/hold windows as the datapath feeds them.
- And the stopwatch times only the laps you run (this stimulus) — for every lap on every path, you need STA (the official timekeeper).
DFF + stopwatch (verified) + datapath = the counter's full-timing run — a dynamic spot-check, not the official STA timing.
5. Working Example — the counter full-timing artifact set
The RTL (tri-HDL) — the 4-bit counter:
// SystemVerilog — 4-bit up counter, sync active-low reset
module counter4 (input logic clk, rst_n, output logic [3:0] count);
always_ff @(posedge clk) if (!rst_n) count <= 4'd0; else count <= count + 4'd1;
endmodule// Verilog-2001 — same counter
module counter4 (clk, rst_n, count);
input clk, rst_n; output reg [3:0] count;
always @(posedge clk) if (!rst_n) count <= 4'd0; else count <= count + 4'd1;
endmodule-- VHDL — same counter
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
entity counter4 is port (clk, rst_n : in std_logic; count : out unsigned(3 downto 0)); end entity;
architecture rtl of counter4 is signal cnt : unsigned(3 downto 0); begin
process (clk) begin
if rising_edge(clk) then
if rst_n = '0' then cnt <= (others => '0'); else cnt <= cnt + 1; end if;
end if;
end process;
count <= cnt;
end architecture;The SDF, annotation + verification, and expected log:
# counter4.sdf — REPRESENTATIVE (MIN:TYP:MAX):
(CELL (CELLTYPE "DFFRX1") (INSTANCE u_cnt/u_q0)
(DELAY (ABSOLUTE (IOPATH CK Q (0.08:0.11:0.15))))
(TIMINGCHECK (SETUP D (posedge CK) (0.04:0.05:0.06)) (HOLD D (posedge CK) (0.02:0.02:0.03))))
# ... u_q1..u_q3, increment cells (IOPATH + INTERCONNECT) ...// Testbench — annotate + VERIFY coverage — REPRESENTATIVE
initial begin
$sdf_annotate("sdf/counter4.sdf", u_dut); // time 0, correct scope (4.3), +maxdelays (4.4)
// ... reset, then stimulus ...
end
// CHECK the annotation log: "0 unmatched" (4.5) -- else the run is silently zero-delay!# Expected log — REPRESENTATIVE (full-timing signature):
# SDF: annotated 10 IOPATH, 1 INTERCONNECT, 2 TIMINGCHECK 0 unmatched (VERIFIED, 4.5)
# t=0 count = xxxx (power-up X)
# t=15 count = 0000 (reset)
# t=25 count 0000->0001 (transition a clk-to-Q delay AFTER the edge, not instant, 3.1)
# ... counting; $setup/$hold checks run, no REAL violations (triaged, 8.4)
# result: FULL-TIMING CLEAN = verified SDF + no real violations + no unexplained X
# note: this is a DYNAMIC spot-check -- exhaustive timing signoff is STA (0.3)Practical context (representative, tool-neutral):
gls/case_counter/
rtl/counter4.v netlist/counter4.vg lib/cells.v sdf/counter4.sdf tb/tb_counter4.v expected/counter4.log
# Flow: synth -> netlist -> compile(+cells+tb) -> $sdf_annotate @0 (VERIFY 0 unmatched, 4.5), corner (3.4),
# MIN:TYP:MAX (4.4) -> full-timing GLS -> diff vs expected.log
# Full-timing clean: verified SDF + no REAL violations (triaged, 8.4) + no unexplained X. NOT STA (0.3).The counter's full-timing signature, as a real waveform:
Counter full-timing GLS: X → reset → count, each transition a real clk-to-Q delay after the edge
9 cycles6. Debugging Session — a "full-timing" run that was silently zero-delay
A full-timing counter signoff is actually silently zero-delay because the SDF never annotated (scope mismatch, unread coverage log), so it verified nothing about timing; verifying annotation (0 unmatched) and re-running restores real timing
VERIFY SDF ANNOTATION (0 UNMATCHED) OR IT'S SILENTLY ZERO-DELAYA counter's full-timing GLS is signed off, but transitions are instant (no clk-to-Q delay) and no timing checks fire — the 'timing' run looks suspiciously like a functional one.
Silently zero-delay — the SDF never annotated. $sdf_annotate failed to match (a wrong scope, 4.3) and no one read the coverage log (4.5), so the counter's specify arcs stayed at placeholder zero (3.1) — the run was functional, not timed, and it verified nothing about timing. The instant transitions and absent timing checks are the tells. This is the 4.5 trap on the case: a full-timing run is only timed if the SDF is verified-annotated (0 unmatched). Signing off 'full-timing' on a silently zero-delay run is signing off nothing about the counter's timing.
Verify SDF annotation: fix the $sdf_annotate scope so the SDF matches, and read the coverage log — confirm 0 unmatched (4.5). Re-run: now transitions land a real clk-to-Q delay after the edge (3.1) and the flops' timing checks run (2.5/3.2). Confirm full-timing clean = verified SDF + no real timing violations (triaged real-vs-artifact, 8.4) + no unexplained X. And keep scope honest — this is a dynamic spot-check (this stimulus, exercised paths), not the exhaustive timing signoff, which is STA (0.3). The lesson: a full-timing GLS is only timed if the SDF is verified-annotated (0 unmatched, 4.5) — always read the coverage log; full-timing clean = verified SDF + no real violations + no unexplained X, and it's a dynamic spot-check, not STA. This case turns 14.1's functional-clean bar into a timing-clean bar. (GLS stays dynamic, 0.3.)
7. Common Mistakes
- Not verifying SDF annotation (0 unmatched) — silently zero-delay (4.5).
- Wrong corner /
MIN:TYP:MAX— setup at fast, hold at slow (3.4/4.4). - Not triaging timing-check firings real-vs-artifact (8.4).
- Treating full-timing GLS as the timing signoff. It's a dynamic spot-check; STA is signoff (0.3).
- Dropping 14.1's
X/reset bar — no unexplainedXstill applies.
8. Industry Best Practices
- Verify SDF annotation (0 unmatched, read the coverage log — 4.5).
- Use the right corner and
MIN:TYP:MAX(3.4/4.4). - Triage timing-check firings (real-vs-artifact, 8.4).
- Define full-timing clean = verified SDF + no real violations + no unexplained
X. - Defer exhaustive timing to STA — GLS is a dynamic spot-check.
Senior Engineer Thinking
- Beginner: "The SDF file is in the run dir, so it's a timing run."
- Senior: "Did
$sdf_annotatematch — 0 unmatched in the coverage log? If not, it's silently zero-delay and verifies nothing about timing. And full-timing clean is verified SDF + no real violations + no unexplainedX— a spot-check, not STA."
The senior verifies annotation coverage and holds the full-timing-clean bar, deferring exhaustive timing to STA.
Silicon Impact
The counter case is where timing enters the signoff — and its central risk is false confidence: a silently zero-delay run (unverified SDF, 4.5) that claims full-timing coverage while verifying nothing, letting a timing-dependent bug reach silicon (0.3) behind a green "timing" run. Verifying annotation (0 unmatched) is the cheap, decisive guard. The case also sets the full-timing-clean bar — verified SDF + no real violations (triaged, 8.4) + no unexplained X — that every timed case (peripherals, SoC) reuses, and it keeps the honest boundary that full-timing GLS is a dynamic spot-check, not STA (0.3). Turning 14.1's functional-clean into 14.2's timing-clean, verifiably, is what makes gate-level timing runs trustworthy.
Engineering Checklist
- Verified SDF annotation (0 unmatched, coverage log — 4.5).
- Used the right corner and
MIN:TYP:MAX(3.4/4.4). - Confirmed the timed signature (transitions a clk-to-Q delay after the edge; checks run).
- Triaged timing-check firings (real-vs-artifact, 8.4).
- Held full-timing clean = verified SDF + no real violations + no unexplained
X; deferred exhaustive timing to STA.
Try Yourself
- Annotate the counter's SDF and read the coverage log — confirm 0 unmatched (4.5).
- Observe: count transitions now land a clk-to-Q delay after the edge (3.1); timing checks run.
- Change: break the
$sdf_annotatescope and re-run without reading the log. - Expect: the run is silently zero-delay (instant transitions, no checks) — a "full-timing" run that verifies nothing. Fix the scope, confirm 0 unmatched, and restore real timing. Verify, don't assume.
Any free Verilog simulator with $sdf_annotate runs the counter full-timing case. No paid tool required.
Interview Perspective
- Weak: "I add the SDF and it's a timing run."
- Good: "I annotate the SDF and see real clk-to-Q delays and timing checks."
- Senior: "A full-timing counter run is only timed if the SDF is verified-annotated — 0 unmatched in the coverage log (else it's silently zero-delay). I use the right corner and
MIN:TYP:MAX, triage timing-check firings real-vs-artifact, and define full-timing clean as verified SDF + no real violations + no unexplainedX. It's a dynamic spot-check — STA is the exhaustive timing signoff."
9. Interview / Review Questions
10. Key Takeaways
- The counter case adds timing to 14.1's flow — a full-timing GLS with SDF (cell + net delays + timing checks,
MIN:TYP:MAX) on a design with real state and a datapath. - Verify SDF annotation (0 unmatched, read the coverage log — 4.5) — or the "full-timing" run is silently zero-delay and verifies nothing about timing.
- The timed signature: power-up
X→ reset → count, with each transition landing a real clk-to-Q delay after the edge (3.1) and the flops' timing checks running (2.5/3.2). - Full-timing clean = verified SDF + no real timing violations (triaged real-vs-artifact, 8.4) + no unexplained
X(14.1's bar still holds) — using the right corner andMIN:TYP:MAX(3.4/4.4). - Full-timing GLS is a dynamic spot-check (this stimulus, exercised paths) — not the exhaustive timing signoff; STA is that (0.3). Next: 14.3 — FSM: X-cleanup & reset signoff.
Quick Revision
Counter case = 14.1 + timing (SDF). Ship RTL/netlist/SDF/TB/log. VERIFY annotation (0 unmatched, 4.5) — else silently zero-delay. Timed signature: X → reset → count, transitions a clk-to-Q delay after the edge (3.1); checks run (2.5/3.2). Right corner (3.4) +
MIN:TYP:MAX(4.4). Full-timing clean = verified SDF + no real violations (triaged, 8.4) + no unexplained X. Dynamic spot-check, not STA (0.3). Next: 14.3 — FSM X-cleanup & reset signoff.