GLS · Chapter 4 · SDF Annotation
Working Example: Annotating the Counter with SDF
This capstone applies the whole SDF flow to the counter you read earlier in the track. It walks a representative counter SDF carrying cell delays, net interconnect delays, and flop timing checks as min, typical, and max triplets. It back-annotates that SDF at time zero, bound to the correct scope, selects the max values for a setup-flavoured run, and verifies full coverage with zero unmatched instances. A before-and-after waveform shows the counter shift from zero-delay to real clock-to-output timing, with count transitions now delayed. The debug session shows what a partially annotated counter looks like when one flop is silently left at zero delay while its siblings are timed, producing subtly inconsistent count timing that only the coverage log can name. This closes the SDF chapter and prepares you to adapt a testbench for gate-level runs.
Foundation13 min readGLSSDFCounterBack-AnnotationWorked Example
Chapter 4 · Section 4.6 · SDF Annotation
Project thread — the counter has travelled from RTL (2.6) through timing concepts (Ch3) to a real annotated run here. Chapter 5 adapts the testbench so this timed counter runs cleanly (reset, X, strobing); the counter carries on toward the FSM and mini-SoC later.
1. Why Should I Learn This?
This is where the chapter's pieces become one repeatable procedure: SDF → annotate → select → verify → timed run.
- You take the counter from zero-delay to timed with confidence.
- You verify it (coverage, 0 unmatched) rather than assume it.
- You recognise a partially annotated counter on sight.
It integrates 4.1–4.5 and closes the SDF chapter.
2. Real Silicon Story — the counter bit that stayed instant
A counter's timed run looked right — most bits updated a clk-to-Q delay after the clock. But one bit kept updating instantly, and an occasional glitch-capture on a decode of that bit slipped through.
That flop's instance had been left unmatched in annotation (a scope/name issue, 4.5), so it ran zero-delay while its siblings were timed. The mixed timing was subtle in the waveform and invisible in the pass/fail — only the coverage log's unmatched line named it.
Lesson: verify the whole counter annotated. One silently-zero flop makes the timed run partly fake — read coverage, demand 0 unmatched.
3. Concept — the counter annotation, end to end
The procedure (integrating the chapter):
- SDF (4.1/4.2): a file for this counter netlist —
IOPATH(flops + increment cells),INTERCONNECT(nets),TIMINGCHECK(flop setup/hold), eachMIN:TYP:MAX. - Annotate (4.3):
$sdf_annotate("counter4.sdf", u_dut)at time 0, scope = the counter instance. - Select (4.4):
MAXfor a setup-flavoured run (orMINfor hold), stated explicitly. - Verify (4.5): read the coverage log — matched counts, 0 unmatched across all four flops and the increment logic.
- Run: the counter now shows real clk-to-Q delay; transitions land a delay after each edge.
What you should see: xxxx at power-up until reset (2.6), then 0,1,2,3,… — but now each count transition occurs a clk-to-Q delay after the clock edge, not instantly.
Scope: representative; a verified annotated run is timed but dynamic — STA (all corners) is signoff (0.3).
4. Mental Model — the counter gets a stopwatch
Pre-annotation, the counter is a silent film — everything happens on the exact clock frame (zero-delay).
- Annotation gives every flop and wire a stopwatch — the SDF delays.
- Now each count transition happens a measured moment after the clock, and data must respect the setup/hold windows.
- Verify every stopwatch is running (0 unmatched) — one stopped stopwatch (unmatched flop) and that bit is back in the silent film while the rest is timed.
5. Working Example — the counter SDF, annotation, coverage, waveform
A representative counter SDF (trimmed to shape):
# counter4.sdf — REPRESENTATIVE, tool-neutral. Values are MIN:TYP:MAX (4.4).
(DELAYFILE (SDFVERSION "3.0") (DESIGN "counter4") (TIMESCALE 1ns) (DIVIDER /)
(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))))
(CELL (CELLTYPE "DFFRX1") (INSTANCE u_cnt/u_q1) (DELAY (ABSOLUTE (IOPATH CK Q (0.08:0.11:0.15)))))
(CELL (CELLTYPE "DFFRX1") (INSTANCE u_cnt/u_q2) (DELAY (ABSOLUTE (IOPATH CK Q (0.08:0.11:0.15)))))
(CELL (CELLTYPE "DFFRX1") (INSTANCE u_cnt/u_q3) (DELAY (ABSOLUTE (IOPATH CK Q (0.08:0.11:0.15)))))
(CELL (CELLTYPE "XOR2X1") (INSTANCE u_cnt/u_x1)
(DELAY (ABSOLUTE (INTERCONNECT u_cnt/u_i0/Y u_cnt/u_x1/A (0.03:0.04:0.06))
(IOPATH A Y (0.05:0.06:0.08))))))The annotation call and the coverage you must confirm:
// TB — REPRESENTATIVE, tool-neutral
initial begin
$sdf_annotate("sdf/counter4.sdf", u_dut); // time 0, scope = counter (4.3)
// ... reset, then stimulus ...
end# Coverage log — REPRESENTATIVE — the check that makes it real (4.5)
SDF: annotated 10 IOPATH, 1 INTERCONNECT, 2 TIMINGCHECK (selection: MAXDELAYS)
SDF: 0 unmatched instances <-- every flop (u_q0..u_q3) + increment cells timedPractical context (representative, tool-neutral):
gls/
netlist/counter4.vg # synthesized counter (2.6)
lib/cells.v # cell models with specify arcs (2.3/2.4)
sdf/counter4.sdf # THIS lesson's artifact
tb/tb_counter4.v # $sdf_annotate + stimulus
# Flow: compile(vg+cells+tb) -> $sdf_annotate @0 (scope u_dut, MAXDELAYS)
# -> confirm 0 unmatched -> run -> timed counter waveformThe counter, zero-delay vs SDF-annotated, as a real waveform:
Counter: zero-delay run vs SDF-annotated (count transitions land a clk-to-Q delay after CK)
9 cycles6. Debugging Session — a partially annotated counter
A timed counter run has one bit updating instantly while the others show clk-to-Q delay — that flop's instance was silently left unmatched during annotation, so it ran zero-delay, and only the coverage log's unmatched line names it
ONE UNMATCHED FLOP = A PARTLY-FAKE TIMED COUNTERThe counter's annotated run mostly shows real clk-to-Q delay, but one bit updates instantly. An occasional glitch-capture on a decode of that bit slips through, and pass/fail looks clean.
Partial annotation (4.5) on the counter. One flop's CELL block did not match — a wrong scope, a DIVIDER/path mismatch (4.2), or a post-ECO rename — so that instance was silently left at zero delay while u_q0..u_q3's siblings were timed. The mixed timing (one bit instant, the rest delayed) is the visible signature; the definitive evidence is the coverage log's unmatched line naming that flop. Nothing errored — the counter was partly timed and fully trusted, exactly the false-confidence trap of 4.5, now on the running design.
Read the coverage log; treat the unmatched flop as a failure. Fix the root cause (correct the $sdf_annotate scope, the DIVIDER, or the renamed instance path so every counter flop matches), re-run, and confirm 0 unmatched across u_q0..u_q3 and the increment cells. Enforce it as policy — fail the run if any counter instance is unannotated. The lesson: annotating the counter is only done when every flop and net is verified matched (0 unmatched); one silently-zero flop makes the timed counter partly fake — the coverage log, not the pass/fail, is the proof. (Verified timed, it is still dynamic — STA closes timing, 0.3.)
7. Common Mistakes
- Trusting a timed counter without coverage. One unmatched flop runs zero-delay silently.
- Annotating at the wrong scope for the counter instance (4.3).
- Not selecting the triplet value (MAX for setup) explicitly (4.4).
- Reading only matched counts, not the unmatched list (4.5).
- Calling the verified timed counter "timing closed." STA is signoff (0.3).
8. Industry Best Practices
- Verify 0 unmatched across all counter flops and increment cells before trusting the run.
- Annotate at time 0, correct scope, explicit
MAX/MINselection. - Keep the counter's power-up-X-until-reset signature (2.6) intact under timing.
- Fail the run on any unmatched counter instance (policy).
- Defer timing closure to STA (all corners); use the timed counter for function-with-delay.
Senior Engineer Thinking
- Beginner: "The counter shows delays now — it's a timed run."
- Senior: "Every flop? One bit updating instantly means an unmatched instance running zero-delay. I read the coverage log, demand 0 unmatched across
u_q0..u_q3, and only then call it timed — and still not closed; that's STA."
The senior verifies whole-counter coverage and separates a verified timed run from timing closure.
Silicon Impact
The counter is the smallest design where the whole SDF flow — and its pitfalls — shows up concretely. A verified annotated counter exercises real clk-to-Q delays, setup/hold windows, and net delays, revealing timing-dependent functional issues (glitch capture, reset-release races) under realistic timing. A partially annotated counter hides them behind a mostly-timed waveform — a silently-zero flop's timing behaviour never gets exercised, and a real bug there can reach silicon as an intermittent failure (0.3). Verifying 0 unmatched is what makes the timed counter trustworthy; STA across corners is what proves its margins.
Engineering Checklist
- Annotated
counter4.sdfat time 0, scope = counter, explicit selection (4.3/4.4). - Verified 0 unmatched across
u_q0..u_q3and the increment cells (4.5). - Confirmed the timed signature:
xxxx→ reset0→ counting, transitions a clk-to-Q delay after CK. - Failed the run on any unmatched counter instance (policy).
- Treated the verified timed run as dynamic — deferred closure to STA (0.3).
Try Yourself
- Compile the counter netlist + cell models + TB and annotate
counter4.sdfat time 0 (scope =u_dut,+maxdelays). - Observe: after reset, each
countbit toggles a clk-to-Q delay after the clock edge, and the coverage log reports 0 unmatched. - Change: rename one flop (e.g.
u_q2) in the netlist to simulate an ECO and re-run. - Expect: that bit reverts to zero-delay (updates instantly), and the log lists it as unmatched — the partially-annotated counter, reproduced. Fix the name and confirm 0 unmatched again.
Any free Verilog simulator runs this end-to-end ($sdf_annotate + coverage log). Real SDF is tool-generated, but the flow is identical. No paid tool required.
Interview Perspective
- Weak: "You give the simulator the counter's SDF and it's timed."
- Good: "You back-annotate the counter SDF at time 0, select MAX/MIN, and the flops show clk-to-Q delay with setup/hold checks."
- Senior: "I annotate at the right scope and time 0, select the triplet value for the check, and — crucially — verify 0 unmatched across every counter flop and net before calling it timed. One silently-zero flop makes it partly fake. And a verified timed counter is still dynamic; STA across corners is closure."
9. Interview / Review Questions
10. Key Takeaways
- Annotating the counter integrates the chapter: SDF (cell + net delays + flop checks,
MIN:TYP:MAX) →$sdf_annotateat time 0, correct scope → selectMAX/MIN→ verify coverage (0 unmatched) → timed run. - The timed counter keeps its power-up-
X-until-reset signature (2.6), but now each count transition lands a clk-to-Q delay after the clock edge. - Verification is the point: read the coverage log and demand 0 unmatched across all flops and increment cells — do not trust a mostly-timed waveform.
- A partially annotated counter (one flop silently zero-delay) is the on-design form of 4.5's trap — one bit updates instantly while the rest is timed; the unmatched line, not pass/fail, names it.
- A verified annotated counter is timed but still dynamic — STA (all corners) is the timing signoff (0.3). This closes Chapter 4; next, Chapter 5 adapts the testbench for GLS.
Quick Revision
Annotate the counter: SDF (cell+net delays+flop checks,
MIN:TYP:MAX) →$sdf_annotate@ time 0, counter scope, select MAX/MIN → verify 0 unmatched (4.5) → timed run. Signature:xxxx→ reset0→ count, now each transition a clk-to-Q delay after CK. One bit instant = an unmatched flop (partial annotation). Verified timed ≠ closed — STA signs off. Chapter 4 complete; next: Chapter 5 — GLS testbench adaptation.