GLS-03: SDF Back-Annotation β€” VLSI Trainers
⌂ Home / GLS Series
Gate-Level Simulation · Article 3 of 7

GLS-03: SDF Back-Annotation

The structure of an SDF file, how delays are annotated into the gate-level netlist, the two annotation methods, MTM corner control, annotation statistics, and how to confirm delays are correctly applied before trusting simulation results.

πŸ”—What Back-Annotation Means

After synthesis and place-and-route, the backend implementation tool knows the exact propagation delay of every signal path in the design β€” derived from standard cell characterisation data and extracted parasitic capacitance and resistance of the physical wires. This timing data must somehow be transferred to the simulator so that simulation reflects real-world behaviour.

Back-annotation is the process of taking timing information from the physical implementation domain and applying it to the gate-level simulation model. The word “back” reflects the direction: information flows backward through the design flow β€” from the physical implementation stage back to the verification stage.

Figure 1 β€” Back-annotation: timing flows backward from physical implementation to simulation
RTL synth Netlist P&R Placed & Routed Netlist + SDF STA Timing Signed Off Silicon SDF Gate-Level Simulation (Netlist + SDF annotated) back-annotation vlsitrainers.com

The design flow runs left to right: RTL β†’ synthesis β†’ P&R β†’ STA β†’ silicon. Back-annotation (red dashed path) moves timing data extracted from the P&R stage backward to the simulation stage. The gate-level netlist and the SDF file β€” both products of the backend flow β€” are combined in GLS to produce simulation that closely predicts silicon behaviour.

The Standard Delay Format (SDF) is the industry-standard file format for carrying this back-annotated timing information. It is a text file, human-readable, and understood by all major HDL simulators. Every delay value in an SDF file corresponds to a specific timing arc or timing check in a specific instance of the netlist.

πŸ“„SDF File Structure

An SDF file has a consistent two-part structure: a header section containing metadata about the design and timing environment, followed by one or more cell entries that contain the actual delay values for each instance.

Figure 2 β€” SDF file structure: header section and cell entry anatomy
HEADER SECTION (DELAYFILE (SDFVERSION “3.0”) (DESIGN “my_chip”) (DATE “Jan 10 2025”) (VENDOR “lib_vendor”) (PROGRAM “pnr_tool”) (VERSION “1.0”) (DIVIDER /) (VOLTAGE 0.8:0.9:1.0) (PROCESS “slow”) (TEMPERATURE -40:25:125) (TIMESCALE 1ps) CELL ENTRY (one per instance) (CELL (CELLTYPE “DFFX1”) (INSTANCE top.u_cpu.u_reg) ; path delays (DELAY (ABSOLUTE (IOPATH (posedge CK) Q (180:210:260) (175:205:255) ) )) ; timing checks (TIMINGCHECK (SETUPHOLD D (posedge CK) (45:52:68)(10:12:15)) ↑ rise delay ↑ fall delay ↑ MIN:TYP:MAX vlsitrainers.com

SDF file structure. The header (left, blue) records metadata: format version, design name, generating tool, process corner, voltage range, temperature range, and the timescale that all delay values are expressed in. Each cell entry (right, green) targets one specific instance in the netlist hierarchy, provides IOPATH delays for each input→output arc, and TIMINGCHECK values. The three-value syntax (180:210:260) represents MIN:TYP:MAX delay.

🏷️SDF Header Fields

Every SDF header field serves a specific purpose. Understanding them is essential for diagnosing mismatches between the SDF and the simulation environment:

Header keywordWhat it specifiesSimulation relevance
SDFVERSION Version of the SDF format used (e.g. “3.0”) Simulator must support this version. If mismatched, annotation may fail silently.
DESIGN Name of the top-level design module Informational β€” helps verify the SDF matches the correct design.
TIMESCALE Unit that all delay values in the file are expressed in (e.g. 1ps, 100fs) Critical. If the simulation timescale precision is coarser than the SDF timescale, delay values will be rounded down β€” potentially to zero. Mismatch here is a common cause of “no delays in waveform” bugs.
VOLTAGE Supply voltage range at which delays were characterised (MIN:TYP:MAX) Documents the PVT corner. Does not affect annotation directly but confirms the SDF matches the intended operating point.
PROCESS Process corner label (e.g. “slow”, “fast”, “tt_1v0_25c”) Confirms which process corner the SDF was generated for. Must match the intended simulation corner.
TEMPERATURE Temperature range at which delays were extracted (MIN:TYP:MAX in degrees Celsius) Together with VOLTAGE and PROCESS, defines the complete PVT operating point. Informational β€” verify it matches intent.
DIVIDER Hierarchy separator character used in instance paths within the SDF (typically / or .) Must match the hierarchy separator used by the simulator for the target design. Mismatch causes all instance path lookups to fail β€” 0% annotation.
TIMESCALE mismatch β€” the silent killer: If the SDF specifies TIMESCALE 1ps and the simulator is running at 1ns/1ns precision, a delay value of 250ps in the SDF becomes 0.25ns at the simulator’s precision β€” which rounds to zero. The annotator reports the annotation as successful (the value was written), but the delay in simulation is zero. The symptom is: SDF annotation statistics show 100% annotated, but no delays appear in waveforms. Always verify that simulation timescale precision is at least as fine as the SDF timescale.

πŸ”’SDF Cell Entries

Below the header, the SDF contains one cell entry for each instance that has timing information. A cell entry has three parts: the cell type, the instance path, and one or more timing constructs:

; Example cell entry
(CELL
  (CELLTYPE "NAND2X1")
  (INSTANCE top.u_alu.u_adder.g42)

  ; Path delays: absolute values override specify block defaults
  (DELAY
    (ABSOLUTE
      (IOPATH A Y (95:112:148) (88:105:139))
      (IOPATH B Y (98:115:152) (91:108:143))
    )
  )
)

CELLTYPE and INSTANCE

CELLTYPE names the cell type β€” this is for reference only and does not affect which instance the delay is applied to. The annotation is driven entirely by INSTANCE, which specifies the full hierarchical path of the target instance in the netlist. The path uses the DIVIDER character to separate hierarchy levels.

DELAY block and ABSOLUTE vs INCREMENT

The DELAY block contains path delay entries. Two modes exist:

IOPATH entries

Each IOPATH entry specifies one timing arc: from a named input pin to a named output pin. The two value triples that follow are the rise delay and fall delay respectively. Both are expressed as (MIN:TYP:MAX) triplets β€” the simulator selects which value to use based on the MTM control setting (see Section S5).

TIMINGCHECK block

; Timing check entries for a flip-flop
(TIMINGCHECK
  ; Setup + hold combined check
  (SETUPHOLD
    D (posedge CK)
    (45:52:68)   ; setup time  MIN:TYP:MAX (ps)
    (10:12:15)   ; hold  time  MIN:TYP:MAX (ps)
  )

  ; Recovery + removal check for async reset
  (RECREM
    (negedge RN) (posedge CK)
    (30:38:50)   ; recovery time
    (8:10:13)    ; removal  time
  )

  ; Minimum clock pulse width
  (WIDTH (posedge CK) (200:220:260))
)
Timing checkWhat it enforcesFailure means
SETUP / SETUPHOLD Data must be stable for at least this long before the active clock edge Data not yet settled when the clock samples β€” captured value unreliable
HOLD / SETUPHOLD Data must remain stable for at least this long after the active clock edge Data changes too soon after clock edge β€” previously captured value corrupted
RECOVERY / RECREM Asynchronous deassert (e.g. reset release) must happen this long before the next clock edge Reset deasserted too close to the clock β€” flip-flop may not exit reset reliably
REMOVAL / RECREM Asynchronous assert must remain stable for this long after the clock edge Reset withdrawn too soon after the clock β€” state uncertain during the transition
WIDTH Minimum pulse width on a clock or asynchronous control pin Clock pulse too narrow β€” flip-flop may not latch correctly

βš™οΈDelay Values β€” MIN:TYP:MAX and MTM Control

Every delay value in an SDF file is expressed as a triplet: (minimum : typical : maximum). These three values correspond to different PVT corners β€” fast process/high voltage/low temperature (min), nominal (typ), and slow process/low voltage/high temperature (max).

When the simulator annotates an SDF file, an MTM (Min-Typ-Max) control setting tells it which value from each triplet to use:

MTM settingValue selected from (a:b:c)When to use
MAXIMUMc (third value) β€” worst-case slowSetup timing signoff at slow corner
MINIMUMa (first value) β€” best-case fastHold timing signoff at fast corner
TYPICALb (middle value) β€” nominalDevelopment debug runs (typical silicon)

If a triplet is given as a single value β€” for example, (250) β€” that single value is used regardless of the MTM setting. Some back-end flows generate per-corner SDF files (one file for slow, one for fast) rather than embedding all three values in a single file, in which case the MTM setting is irrelevant.

Figure 3 β€” MTM control: which value is selected from a MIN:TYP:MAX triplet
180 MIN (fast) : 220 TYP (nominal) : 280 MAX (slow) MTM = MINIMUM β†’ selects 180ps hold corner MTM = MAXIMUM β†’ selects 280ps setup corner MTM = TYPICAL β†’ selects 220ps (debug) vlsitrainers.com

MTM control selects one value from each MIN:TYP:MAX triplet. MINIMUM selects the fastest (best-case) delays β€” used to stress hold timing. MAXIMUM selects the slowest (worst-case) delays β€” used for setup timing signoff. TYPICAL selects the middle value for representative development simulation. The same SDF file can be used for all three corners by changing only the MTM control setting.

Scale factors: Some annotation flows allow a global scale factor to be applied to all delay values β€” for example, to derate delays by 10% for margin. A scale factor of 0.9 multiplies every annotated delay by 0.9 before applying it to the netlist. This can be specified per-delay-type (IOPATH separately from timing checks) and is useful for applying OCV (On-Chip Variation) derating without regenerating the SDF file.

πŸ”ŒTwo Annotation Methods

There are two ways to tell the simulator to read and apply an SDF file. Both achieve the same result β€” delays annotated into the netlist before simulation begins β€” but they differ in where the annotation instruction lives and which takes precedence when both are present.

Figure 4 β€” Method 1: system task in testbench vs Method 2: command file at simulator invocation
Method 1 β€” System Task in Testbench Inside the testbench HDL file: initial begin $sdf_annotate( “delays.sdf”, top.dut, // scope Characteristics: β€’ Annotation instruction lives in the testbench β€’ Executed at time 0 during elaboration Method 2 β€” SDF Command File A separate text file passed at simulator invocation: // sdf_cmd_file contents: SDF_FILE “delays.sdf” SCOPE top.dut MTM_CONTROL MAXIMUM // selects MAX delays Characteristics: β€’ External to testbench β€” no TB change needed β€’ Takes precedence over Method 1 if both present vlsitrainers.com

Method 1 (left): the $sdf_annotate system task is placed inside the testbench initial block. It is executed at time 0 during elaboration, reading the named SDF file and annotating the specified scope. Method 2 (right): a separate SDF command file is passed as a simulator argument. It supports all the same options plus explicit MTM control and does not require any testbench modification. When both are present, Method 2 takes precedence.

Method 1 β€” $sdf_annotate system task

The $sdf_annotate system task is called inside the testbench at time 0. The simulator recognises this task during elaboration and automatically applies the SDF before simulation begins. Arguments include:

ArgumentRequired?Purpose
sdf_fileYesPath to the SDF file to annotate
scopeNoThe module instance to annotate. Defaults to the scope where the task is called. Must match the hierarchy in the SDF INSTANCE paths.
log_fileNoName of the annotation log file β€” records every arc annotated. Essential for debugging.
mtm_specNoMTM control: "MINIMUM", "TYPICAL", or "MAXIMUM". Defaults to simulator’s built-in default.
scale_factorsNoScale factor string applied to all delays, e.g. "1.0:1.0:1.0"

Method 2 β€” SDF command file

A plain-text command file passed to the simulator. This approach keeps annotation configuration outside the testbench β€” useful when the same testbench is reused across multiple GLS runs at different corners or with different SDF files. Common keywords in an SDF command file:

KeywordPurpose
SDF_FILEPath to the SDF file (uncompiled β€” simulator compiles it automatically)
COMPILED_SDF_FILEPath to a pre-compiled SDF binary β€” faster startup for large files
SCOPEInstance path in the netlist where annotation begins
MTM_CONTROLMINIMUM, TYPICAL, or MAXIMUM β€” which delay value from each triplet to use
SCALE_FACTORSDerating multipliers for min, typ, and max delays
LOG_FILEDestination for the detailed annotation log
Precedence rule: When both a $sdf_annotate call in the testbench and an SDF command file are present, the command file takes priority and the system task is ignored. The simulator issues a warning noting that the system task was suppressed. This precedence rule allows the same testbench to be used for both RTL simulation (no command file) and GLS (command file present) without modifying the testbench source.

🎯Annotation Scope

The annotation scope is the hierarchical instance path in the simulation that the SDF applies to. Understanding scope is critical because the instance paths in the SDF file are relative to the annotation scope β€” they identify cells within the scope’s subtree.

Consider a typical testbench structure:

// Testbench hierarchy:
module tb_top;          // testbench top level
  my_chip dut(…);       // device under test
    // dut contains: u_cpu, u_mem, u_periph…
endmodule

// SDF INSTANCE paths inside the SDF file:
(INSTANCE u_cpu.u_alu.g42)   // relative to annotation scope
(INSTANCE u_mem.u_sram.q0)   // relative to annotation scope

If the annotation scope is set to tb_top.dut, then the SDF instance path u_cpu.u_alu.g42 resolves to tb_top.dut.u_cpu.u_alu.g42 in the simulation hierarchy β€” which is the correct gate instance. If the scope is wrong, every instance path in the SDF will fail to resolve and annotation will be 0%.

Common scope errors: (1) Scope points to testbench top (tb_top) instead of the DUT instance (tb_top.dut) β€” SDF instance paths skip the DUT level and resolve to nothing. (2) Scope matches the DUT but the SDF was generated with a different instance hierarchy (synthesis preserved a different set of hierarchy levels) β€” SDFNEP warnings fire for every mismatched path. (3) The DIVIDER character in the SDF header doesn’t match the simulator’s hierarchy separator β€” all paths fail silently.

πŸ“ŠReading Annotation Statistics

After annotation completes, the simulator prints a statistics summary showing how many timing arcs and checks were successfully annotated. This is the first thing to examine after any SDF GLS run β€” before looking at simulation results.

// Example annotation statistics output:

Annotating SDF timing data:
  Compiled SDF file:   delays.sdf.X
  Log file:            sdf.log
  Backannotation scope: tb_top.dut
  MTM control:         MAXIMUM
  Scale factors:       1.0:1.0:1.0

Annotation completed successfully…

SDF statistics:
  Path Delays:   Specified = 2840  Annotated = 2840  (100.00%)
  $setuphold:    Specified = 960   Annotated = 960   (100.00%)
  $recrem:       Specified = 240   Annotated = 240   (100.00%)
  $hold:         Specified = 120   Annotated = 0     (  0.00%)
  $width:        Specified = 480   Annotated = 0     (  0.00%)

Reading this output:

Figure 5 β€” Annotation statistics triage: how to interpret partial annotation percentages
Path Delays < 100% ? YES β€” investigate Likely causes: β‘  Wrong annotation scope β€” paths don’t resolve β‘‘ DIVIDER mismatch in SDF header β‘’ SDF generated from different netlist version β‘£ Instance was flattened / renamed by synthesis β‘€ Edge specifier mismatch (SDF has posedge, model has none) β†’ Enable verbose annotation log for exact paths NO β€” OK Timing Checks < 100% ? May be expected or a problem: βœ“ SETUPHOLD in SDF vs $setup/$hold in model βœ“ Width checks not always in SDF (flow-dependent) βœ— 0% on all checks β†’ scope or library model issue βœ— Safety-critical checks missing β†’ must investigate vlsitrainers.com

Annotation statistics triage. Path delays below 100% are always a problem requiring investigation β€” common causes are scope mismatch, hierarchy separator mismatch, or a stale SDF. Timing check percentages below 100% may or may not be problems: combined $setuphold vs separate $setup/$hold mismatches are common and expected; $width checks at 0% may be normal for the flow; 0% across all check types alongside 100% path delays suggests a library model vs SDF mismatch on the timing check structure.

πŸ”Confirming Delays Are Applied

100% annotation statistics are necessary but not sufficient to confirm delays are working correctly. As noted in the TIMESCALE section, values can be annotated but rounded to zero due to precision mismatch. Three verification steps confirm delays are genuinely active:

Step 1 β€” Check the annotation log file

With verbose annotation logging enabled, the log file records every arc that was annotated and the value written. Examine a representative flip-flop instance and verify the logged delay values match expectations from the SDF file.

// Example annotation log entries:
Time units: 1ps

Annotating to instance tb_top.dut.u_cpu.u_reg of module DFFX1
  ABSOLUTE (IOPATH (posedge CK) Q) = (180:210:260) rise
  ABSOLUTE (IOPATH (posedge CK) Q) = (175:205:255) fall
  SETUPHOLD D (posedge CK) = (45:52:68) setup (10:12:15) hold

Step 2 β€” Observe a waveform dump

Run the simulation with waveform dumping enabled and probe the output pin of a cell that has a known, non-trivial SDF delay. Measure the propagation time from the input transition to the output transition. It should match the SDF value (at the selected MTM corner) multiplied by the scale factor.

πŸ” Worked Example β€” Verifying a Delay Is Active

SDF entry: IOPATH A Y (95:112:148)(88:105:139) for instance top.dut.u1.g5. MTM = MAXIMUM. Timescale = 1ns/1ps.

Expected delay: Rise = 148ps (MAX). Fall = 139ps (MAX).

In waveform viewer: Drive input A high at time 10.000ns. Measure output Y rising edge. It should occur at approximately 10.148ns.

If Y rises at 10.000ns (zero delay): The delay was not annotated. Check: (1) Is timescale precision fine enough? 1ns/1ps is fine for 148ps. (2) Is verbose log showing the annotation? (3) Is the instance path in the SDF exactly matching the simulation hierarchy? Enable the annotation dump timing file option to list all annotated values per instance.

If Y rises at 10.112ns: Simulation is using the TYP value (112ps), not MAX. The MTM control setting is TYPICAL, not MAXIMUM. Verify the MTM setting in the SDF command file or the $sdf_annotate call.

Step 3 β€” Use the timing dump option

Most simulators offer an option to dump the complete timing information of the design to a text file after elaboration. This file lists every instance in the simulation hierarchy alongside all annotated timing arcs and check values. Searching this file for a known instance path confirms exactly what the simulator is using β€” directly from the elaborated simulation model, bypassing any uncertainty about whether the log file reflects the actual simulation state.

πŸ”¬VLSI Connections

πŸ”¬ SDF generation in the backend flow β€” what produces it and when

In a typical SoC physical implementation flow, SDF files are generated at two points. The first is immediately after synthesis, before routing, using estimated wire delays β€” this is sometimes called a “pre-layout” or “estimated parasitics” SDF. Its path delays include accurate cell intrinsic delays from characterisation but use statistical wire delay estimates because routing has not yet occurred. This SDF is useful for early zero-delay or estimated-delay GLS but should never be used for final signoff. The second and definitive SDF is generated after static timing analysis on the post-route parasitic extraction β€” this SDF contains actual measured interconnect delays and is the correct file for signoff GLS. The difference in wire delays between pre-layout and post-layout SDF can be large β€” particularly for long nets crossing multiple metal layers. Always confirm with the backend team which SDF was delivered before interpreting GLS timing results.

πŸ”¬ Why annotation scope matters in multi-instance designs

In large SoC designs, the same macro or IP block is often instantiated multiple times β€” for example, four identical CPU cores. Each instance has a unique hierarchical path (e.g. top.core0, top.core1…) and the SDF contains separate cell entries for each instance, since the physical layout of each core is different (different placement, different routing, different parasitic values). If the annotation scope is set incorrectly β€” for example, to top instead of the individual core β€” the annotator may map one core’s SDF entries to another core’s instances, producing incorrect timing values without generating any errors. The annotation percentage stays at 100% because the instance paths resolve (they just resolve to the wrong instances). The symptom is that all four cores pass GLS but silicon shows timing failures on specific cores. This is one reason why multi-core designs often annotate each core separately with explicitly scoped annotation calls.

πŸ”¬ Pre-compiled SDF and GLS run time reduction

For large designs, parsing and compiling a multi-megabyte SDF text file at the start of every simulation run adds significant overhead β€” sometimes minutes per run. Most simulators support pre-compiling the SDF into a binary format once, then loading the binary in subsequent runs. The pre-compiled file is typically several times smaller than the text SDF and loads orders of magnitude faster. In a GLS regression with hundreds of test cases, switching to pre-compiled SDF can reduce total simulation time by 20–40% simply by eliminating repeated SDF parsing. The compiled file must be regenerated whenever the SDF changes (i.e., when a new netlist or new timing extraction is delivered by the backend team). A simple Makefile dependency on the SDF file timestamp ensures the compiled version stays current automatically.

Summary β€” GLS-03 key points: Back-annotation transfers timing data from the physical implementation (P&R, STA) backward to the simulation stage via an SDF file. An SDF file has a header section (SDFVERSION, DESIGN, TIMESCALE, VOLTAGE, PROCESS, TEMPERATURE, DIVIDER) and cell entries (CELLTYPE, INSTANCE, DELAY with IOPATH arcs, TIMINGCHECK with SETUPHOLD/RECREM/WIDTH). Every delay value is a MIN:TYP:MAX triplet; MTM control selects which value to use (MAXIMUM for setup signoff, MINIMUM for hold, TYPICAL for debug). Two annotation methods: $sdf_annotate system task in testbench (executed at time 0), and SDF command file passed at invocation (higher precedence, no testbench change needed). Annotation scope specifies the DUT hierarchy root β€” must exactly match the instance paths in the SDF. Annotation statistics show percentage of paths and timing checks successfully annotated β€” 100% on path delays is necessary before trusting timing results. TIMESCALE mismatch causes delays to round to zero even when annotation reports 100% β€” always verify with a waveform delay measurement.
Scroll to Top