Skip to content

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), each MIN:TYP:MAX.
  • $sdf_annotate at time 0, correct scope; verify coverage (0 unmatched, 4.5).
  • Corner (3.4): use the intended corner (setup→slow, hold→fast); MIN:TYP:MAX selection (4.4).

The timed signature:

  • Power-up X → reset 0 → count 0,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.
Counter + SDF flow: annotate and verify coverage, right corner, MIN:TYP:MAX, full-timing GLS; clean = verified SDF + no real violations + no unexplained Xverifyspot-check (not STA)Counter netlist +modelsflops + increment (2.6)SDFIOPATH + INTERCONNECT +checks (MIN:TYP:MAX)Annotate + VERIFYtime 0, scope; 0 unmatched(4.5); corner (3.4)Full-timing GLSreal clk-to-Q + timingchecksTimed signatureX→reset→count, transitionsa clk-to-Q delay after edgeFull-timing cleanverified SDF + no realviolations + no unexplainedX12
Figure 1 - the counter full-timing case (representative). The counter netlist + cell models + an SDF (IOPATH cell delays + INTERCONNECT net delays + SETUP/HOLD checks, MIN:TYP:MAX) -> $sdf_annotate at time 0 (VERIFY coverage: 0 unmatched, 4.5), right corner (3.4), MIN:TYP:MAX (4.4) -> full-timing GLS. Timed signature: power-up X -> reset -> count, each transition a real clk-to-Q delay after the edge; timing checks run. FULL-TIMING CLEAN = verified SDF + no real violations (triaged, 8.4) + no unexplained X. It is a DYNAMIC spot-check, not STA (exhaustive signoff).

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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# 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) ...
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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!
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# 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):

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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 cycles
After reset the counter increments, and each count transition occurs a clk-to-Q delay after the clock edgereset → 0000reset → 0000transitions a clk-to-Q delay after edge (SDF)transitions a clk-to-Q…clkrst_ncount[0] (timed)XXcount[1] (timed)XXt0t1t2t3t4t5t6t7t8
Representative. Power-up X (14.1's bar), reset to 0000, then counting — but now each count transition lands a real clk-to-Q delay AFTER the clock edge (SDF, 3.1), not instantly, and the flops' $setup/$hold checks run (2.5/3.2). Full-timing clean = verified SDF (0 unmatched) + no real violations + no unexplained X. It's a dynamic spot-check; STA is the exhaustive timing signoff.

6. Debugging Session — a "full-timing" run that was silently zero-delay

1

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-DELAY
Symptom

A 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.

Root Cause

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.

Fix

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 unexplained X still 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_annotate match — 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 unexplained X — 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 barverified 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

  1. Annotate the counter's SDF and read the coverage log — confirm 0 unmatched (4.5).
  2. Observe: count transitions now land a clk-to-Q delay after the edge (3.1); timing checks run.
  3. Change: break the $sdf_annotate scope and re-run without reading the log.
  4. 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 unexplained X. 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 and MIN: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.