Skip to content

GLS · Chapter 4 · SDF Annotation

The SDF Back-Annotation Flow

An SDF file on disk does nothing until it is applied. Back-annotation is the step that reads the SDF and overrides each cell model's placeholder delays with the file's real per-instance values. This lesson walks the flow: where the SDF comes from in delay calculation and STA, how the annotate call binds it to a scope in the netlist, when in the run it must happen, and how the annotator matches each SDF entry to a timing arc. Two things must be right. The annotation must run at time zero, before any meaningful activity, and the scope must point at the correct instance so the paths resolve. Miss the call, or aim it at the wrong scope, and there is no error at all: you simply get a silent zero-delay run.

Foundation11 min readGLSSDF$sdf_annotateBack-AnnotationFlow

Chapter 4 · Section 4.3 · SDF Annotation

Project thread — 4.1/4.2 described the SDF; this lesson is the action that puts its delays onto the counter's arcs. 4.6 runs the full annotated counter.

1. Why Should I Learn This?

Back-annotation is the moment a gate run becomes timed. Get the step wrong and everything downstream is zero-delay.

  • $sdf_annotate binds the SDF to a scope and overrides specify placeholders.
  • It must run at time 0, before meaningful activity.
  • A missing call or wrong scope is a silent zero-delay run — no error.

This connects the SDF file (4.1/4.2) to a real timed simulation and sets up triplet selection (4.4) and debug (4.5).

2. Real Silicon Story — the "timed" run that never annotated

A regression labelled "post-layout timing GLS" passed. Later, someone noticed the runtime was suspiciously fast and the waveforms had zero clk-to-Q delay.

The $sdf_annotate call had been dropped from the run script. The simulation compiled fine, ran fine, and reported no error — but with the SDF never applied, every specify arc stayed at its placeholder zero. The whole "timing" regression had been zero-delay the entire time.

Lesson: having an SDF is not using it. If $sdf_annotate doesn't run (or targets the wrong scope), the run is silently zero-delay — verify annotation, don't assume it.

3. Concept — the back-annotation flow

Step by step (tool-neutral):

  1. Generate the SDF — delay calc / STA writes it from the per-corner .lib + SPEF (4.1).
  2. Compile — netlist + cell models (with specify arcs) + testbench.
  3. Annotate — call $sdf_annotate("file.sdf", scope) at time 0:
    • scope = the instance the SDF's paths are relative to (often the DUT top).
    • The annotator matches each SDF entry to a specify arc / timing check and overrides the placeholder value.
  4. Run — the simulation now propagates with real delays.

Key points:

  • Timing of the call matters: annotate before meaningful activity (time 0). Annotating late leaves early activity untimed.
  • Scope matters: the SDF paths (4.2) resolve relative to the scope; wrong scope → no match → zero delay.
  • Command/config file: flows often use an SDF command file to pick corner, scaling, and MIN:TYP:MAX (4.4).

Scope of the result: the run is now timed, but still dynamic/stimulus-limited — STA is signoff (0.3).

Flow: delay calc generates SDF; compile netlist and models; $sdf_annotate at time 0 binds to scope and overrides specify placeholders; run with real delaysresolve pathsif not annotatedDelay calc / STA→ SDFCompile netlist +models + TB$sdf_annotate(file,scope) @ time 0Match SDF → specifyarcs, OVERRIDEplaceholdersRun: real delaysMissing call /wrong scope →silentzero-delay
Figure 1 — the SDF back-annotation flow (representative, tool-neutral). Delay calculation/STA generates the SDF from the per-corner .lib and SPEF. The netlist, cell models, and testbench are compiled. At time 0, $sdf_annotate binds the SDF to a scope; the annotator matches each SDF entry to a specify arc / timing check and overrides its placeholder value. The run then propagates with real delays. A missing call or wrong scope silently leaves the run zero-delay.

4. Mental Model — annotation is loading the ammunition

The netlist + models are a gun with empty chambers — the specify arcs are there but hold zero.

  • $sdf_annotate is loading the ammunition — the real delays — into those chambers.
  • Load at the start (time 0) or the first shots fire blank (early activity untimed).
  • Aim at the right scope or the rounds go nowhere (paths don't resolve → zero delay).

An unloaded gun still "fires" (the sim runs) — it just does nothing timing-wise. That's the silent zero-delay trap.

5. Working Example — the annotate call, and before/after

A representative annotation call and run skeleton:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Testbench back-annotation — REPRESENTATIVE, tool-neutral
module tb_counter4;
  // ... clock, reset, DUT instance u_dut ...
  initial begin
    // Annotate at time 0, BEFORE stimulus. Scope = the DUT the SDF paths are relative to.
    $sdf_annotate("sdf/counter4_tt.sdf", u_dut);
    // ... apply reset, then stimulus ...
  end
endmodule

A representative annotation log (the confirmation you must read):

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# Simulator annotation log — REPRESENTATIVE, tool-neutral
SDF: reading  sdf/counter4_tt.sdf  (scope: tb_counter4.u_dut)
SDF: annotated 42 IOPATH, 15 INTERCONNECT, 8 TIMINGCHECK entries
SDF: 0 unmatched instances
# If this log is ABSENT, $sdf_annotate never ran -> zero-delay run. READ it.

Practical context (representative, tool-neutral):

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# Two common ways to annotate (tool-neutral):
#   1) in the TB:   $sdf_annotate("sdf/counter4_tt.sdf", u_dut);   // scope = u_dut
#   2) SDF command file: selects corner, MIN:TYP:MAX (4.4), scaling, scope
# Order that matters:  compile -> annotate @ time 0 -> reset -> stimulus -> run
# Always confirm the annotation LOG (matched counts, 0 unmatched) before trusting timing.

Annotated vs not, as a real waveform:

With annotation (real clk-to-Q delay) vs without (silent zero-delay)

8 cycles
When $sdf_annotate runs, the flop output is delayed; when it is skipped, the output changes instantly with no errorCK risesCK ris…annotated → delayedannotated → delayedCKQ (annotated)Q (not annotated)t0t1t2t3t4t5t6t7
Representative. When $sdf_annotate runs at time 0, Q updates a real clk-to-Q delay after CK. When the call is missing (or aimed at the wrong scope), the arcs stay at placeholder zero and Q updates instantly — with no error. Only the annotation log distinguishes the two.

6. Debugging Session — a timing regression that never annotated

1

A post-layout timing regression is actually zero-delay because $sdf_annotate was never called (or targeted the wrong scope) — the run compiled and passed with no error, but every specify arc stayed at placeholder zero

NO ANNOTATION (OR WRONG SCOPE) = SILENT ZERO-DELAY
Symptom

A "post-layout timing GLS" regression passes, but runtime is suspiciously fast and waveforms show zero clk-to-Q delay.

Root Cause

The $sdf_annotate call never applied the SDF — it was omitted from the run, ran after activity instead of at time 0, or pointed at the wrong scope so the SDF's instance paths (4.2) did not resolve. In every case the annotator applied nothing, and each specify arc kept its placeholder zero. The simulation compiled and ran with no error — a zero-delay functional run mislabelled as timing. The tell is the absence of a successful annotation log (or a nonzero "unmatched" count).

Fix

Call $sdf_annotate at time 0 with the correct scope (the instance the SDF paths are relative to), and read the annotation log — confirm nonzero matched counts and 0 unmatched. Wire this check into the flow so a missing/failed annotation fails loud, not silent. The lesson: back-annotation is an explicit step — no $sdf_annotate (or wrong scope/late timing) means a silent zero-delay run; verify via the annotation log. (Partial/failed-match debugging is 4.5.) And a timed run is still dynamic — STA is signoff (0.3).

7. Common Mistakes

  • Assuming compiling the SDF path annotates it. You must call $sdf_annotate.
  • Annotating late (after activity) — early events run untimed.
  • Wrong scope — SDF paths don't resolve → zero delay (4.2).
  • Not reading the annotation log — the only proof it worked.
  • Calling a zero-delay run "timing" because an SDF exists on disk.

8. Industry Best Practices

  • Annotate at time 0 with the correct scope.
  • Always read the annotation log (matched counts, 0 unmatched).
  • Make missing/failed annotation fail loud in the flow.
  • Use an SDF command file for corner/scaling/MIN:TYP:MAX (4.4).
  • Label runs as annotated vs zero-delay — never conflate.

Senior Engineer Thinking

  • Beginner: "The SDF is in the run directory, so it's a timing run."
  • Senior: "Did $sdf_annotate run at time 0 with the right scope? What does the annotation log say — matched counts, unmatched zero? On disk isn't applied."

The senior verifies the annotation step and its log, not just the file's presence.

Silicon Impact

A regression that believes it is timing-verified but is silently zero-delay gives false confidence across an entire test suite — timing-dependent behaviour (glitches, races, check firings) is never exercised with real delays, and such bugs can reach silicon as intermittent failures (0.3). Because the failure is silent (compiles, runs, passes), the only guard is verifying the annotation step — the call, the scope, the log. Treating back-annotation as an explicit, checked step is what makes a "timing GLS" actually timed.

Engineering Checklist

  • Called $sdf_annotate at time 0 with the correct scope.
  • Read the annotation log — nonzero matched, 0 unmatched.
  • Made missing/failed annotation fail loud in the flow.
  • Selected corner / MIN:TYP:MAX via command file where needed (4.4).
  • Labelled the run as annotated (not just "SDF present").

Try Yourself

  1. Compile a small netlist + models + TB without $sdf_annotate and run — note zero clk-to-Q delay and fast runtime.
  2. Observe: no error, but the run is zero-delay.
  3. Change: add $sdf_annotate("...", u_dut); at time 0 with a correct SDF and scope.
  4. Expect: real delays appear, and the annotation log reports matched counts. Then aim the scope at the wrong instance and watch it silently revert to zero-delay.

Any free Verilog simulator implements $sdf_annotate and prints an annotation log. No paid tool required.

Interview Perspective

  • Weak: "You point the simulator at the SDF and it's timed."
  • Good: "You call $sdf_annotate with the SDF and a scope; it overrides the specify placeholders with real delays."
  • Senior: "Back-annotation is an explicit step at time 0, bound to the right scope so the SDF paths resolve. A missing call or wrong scope is a silent zero-delay run — so I verify via the annotation log (matched counts, zero unmatched), and I keep it dynamic, not a timing signoff."

9. Interview / Review Questions

10. Key Takeaways

  • Back-annotation is the explicit step that reads the SDF and overrides each cell's placeholder specify delays (3.1) with real per-instance values.
  • $sdf_annotate("file.sdf", scope) binds the SDF to a scope (the instance its paths are relative to) and the annotator matches entries to specify arcs / timing checks.
  • It must run at time 0 (before meaningful activity), with the correct scope — otherwise SDF paths don't resolve.
  • A missing call, late timing, or wrong scope yields a silent zero-delay run — no error; verify via the annotation log (matched counts, 0 unmatched).
  • A successfully annotated run is timed but still dynamic — STA is the signoff (0.3). Next: 4.4 — MIN:TYP:MAX and delay selection.

Quick Revision

Back-annotation applies the SDF. $sdf_annotate("file", scope) at time 0 overrides specify placeholders with real delays; the annotator matches entries to arcs via the scope. No call / wrong scope / late = silent zero-delay (no error) — verify the annotation log (matched, 0 unmatched). Timed but still dynamic; STA signs off. Next: 4.4 — MIN:TYP:MAX selection.