GLS-06: Common Issues Part 1 — VLSI Trainers
⌂ Home / GLS Series
Gate-Level Simulation · Article 6 of 7

GLS-06: Common Issues Part 1

Four of the most frequent GLS failures — zero-delay gate oscillation loops, CUVUNF hierarchy errors from synthesis flattening, FLFFNF compiled SDF file errors, and SDFNEP/SDFNET annotation path mismatch warnings. Root causes, diagnostics, and resolutions.

🗺️Issue Overview

Gate-level simulation failures fall into two broad categories: problems that prevent the simulation from running at all (compile and elaboration errors), and problems that let it run but produce incorrect or misleading results (annotation warnings and functional failures). This article covers four of the most common issues encountered when first bringing up a GLS environment — each with its own distinct error message, root cause, and resolution strategy.

Figure 1 — Four common GLS issues: when they appear and what they indicate
Compile / Elaborate Elaboration / Annotation Simulation Run Issue 1 Zero-delay gate oscillation loop TRZDGOC Issue 2 CUVUNF hierarchy name not found CUVUNF Issue 3 FLFFNF compiled SDF not found FLFFNF Issue 4 SDFNEP / SDFNET path / check not found SDFNEP · SDFNET vlsitrainers.com

Four common GLS issues mapped to the simulation timeline. CUVUNF (hierarchy name error) appears at elaboration when the testbench references a path flattened by synthesis. FLFFNF (compiled SDF not found) appears during annotation when the pre-compiled SDF binary is missing. TRZDGOC (zero-delay oscillation) appears during simulation when a feedback loop spins infinitely. SDFNEP/SDFNET warnings appear during annotation when SDF paths or checks do not match the HDL model.

🔁Issue 1 — Zero-Delay Gate Oscillation

Issue 1 Zero-delay gate oscillation — simulation hangs or loops infinitely TRZDGOC

Symptom

The simulation starts but does not progress — it runs indefinitely at the same simulation time point, consuming CPU at 100% and producing no further output. The simulator may issue a warning such as: “Possible zero-delay gate oscillation detected.” The simulation time counter stops advancing.

Root cause

In zero-delay simulation, all gate delays are zero. If a combinational feedback loop exists in the netlist — where the output of a gate (directly or through several gates) feeds back to its own input — the loop has no delay to break the cycle. Each evaluation triggers another evaluation at the same simulation time, creating an infinite event loop that the simulator cannot escape.

This is not always a design error. Many feedback loops are legitimate (flip-flop models contain internal feedback, for example) but are made safe by having a non-zero delay somewhere in the loop. Zero-delay mode removes all those delays, potentially activating loops that would be harmless in real silicon.

Figure 2 — Zero-delay feedback loop: how a NOR gate oscillates when i1 changes
i1 b2 (nor) o1 b1 (buf) o2 feedback: o2 drives NOR input With zero delays: At t=10: i1 transitions 1→0 NOR(0, 0) → o1=1 (t=10) buf(1) → o2=1 (t=10) NOR(0, 1) → o1=0 (t=10) buf(0) → o2=0 (t=10) → loops forever at t=10 ∞ With non-zero delay: loop breaks naturally vlsitrainers.com

Zero-delay oscillation. With buf b1 and nor b2 cross-coupled, when i1 transitions from 1 to 0, the NOR output changes, the buffer propagates it back to the NOR input, which changes again — all at the same simulation time t=10 with zero delay. In real silicon, the propagation delays through the buf and NOR gates naturally space out these events in time, preventing the infinite loop. Zero-delay mode removes this temporal separation.

Resolution Detect the loop, identify the driver, add delay or fix the design

Step 1 — Enable loop detection at elaboration

Add the -gateloopwarn switch to elaboration. With this flag, the simulator detects potential zero-delay loops during simulation and interrupts execution with a warning rather than hanging indefinitely. Also add -access +c (or the equivalent driver access flag) to enable interactive interrogation of driver signals.

// Elaboration with loop detection enabled:
elaborate tb_top \
  -nospecify \
  -gateloopwarn    // interrupt on zero-delay oscillation
  -access +rwc     // enable driver/load interrogation

Step 2 — Identify the oscillating net

When the simulator interrupts with the oscillation warning, it drops into interactive mode. Run the TCL command to list all currently active drivers on the oscillating net:

// Interactive TCL command when simulation halts:
drivers -active

// Example output:
// [./top.v, 7, top] buf b1 (o2, o1)
// This identifies buf b1 as the currently active driver in the loop

Step 3 — Determine the resolution

Once the looping circuit is identified, choose the appropriate resolution:

  • Add a delay to break the loop: Insert a small HDL delay (e.g. #0.1) on the looping gate in the netlist or testbench wrapper. This is the quickest fix for simulation purposes. Alternatively, use -seq_udp_delay or -sequdp_nba_delay switches to add a uniform delay to all sequential UDP-modelled primitives (commonly used for flip-flop models).
  • Switch to unit delay mode: Unit delay naturally breaks zero-delay loops by assigning 1 time unit to every element. If the loop disappears under unit delay, it was a false race; if the oscillation persists, the circuit design itself has a fundamental problem.
  • Fix the design: If the loop represents genuine combinational feedback that should not exist, the RTL or synthesis constraints must be corrected. Adding simulation delay does not fix silicon — it only hides the problem in simulation.
// Option A: add sequential UDP delay globally
elaborate tb_top -nospecify -seq_udp_delay 1

// Option B: switch to unit delay mode
elaborate tb_top +delay_mode_unit
Adding a delay hides the oscillation but does not fix silicon. If the design has a genuine combinational loop, the loop exists in the final silicon too — it will oscillate continuously at microwave frequencies, consuming power and producing incorrect outputs. The correct response to a genuine loop is to fix the RTL. Use delay addition only as a temporary measure to proceed with other GLS debugging while the design team investigates the root cause.

🌳Issue 2 — CUVUNF Hierarchy Error

Issue 2 Hierarchical name could not be resolved — synthesis flattened the path CUVUNF

Symptom

The elaboration phase reports an error that a hierarchical name in the testbench cannot be resolved. The error message reads approximately: “The indicated hierarchical name could not be resolved. The name was resolvable up to, but not including, the last component of the name prefix printed in the error message.”

Error: CUVUNF — Hierarchical name 'tb_top.dut.u_cpu.u_alu.carry_internal'
       could not be resolved.
       Resolved up to: 'tb_top.dut.u_cpu'
       // u_alu no longer exists as a separate hierarchy level in the netlist

Root cause

During synthesis, the tool flattens the RTL hierarchy. Sub-modules that existed as separately named hierarchies in the RTL are merged into their parent — all the logic is still present but the hierarchical boundaries are gone. If the testbench references signals using their RTL hierarchical path (e.g. tb_top.dut.u_cpu.u_alu.carry_internal), that path no longer exists in the netlist because u_alu was dissolved into u_cpu during synthesis flattening.

Figure 3 — RTL hierarchy vs post-synthesis flattened netlist: what the testbench path finds
RTL Hierarchy u_cpu u_alu u_reg carry_internal TB path resolves ✓ u_cpu.u_alu.carry_internal synthesis flattens u_alu Post-Synthesis Netlist u_cpu u_alu merged into u_cpu NAND2X1 g4, g7, g12 … (no u_alu scope) net_carry_347 (renamed) TB path FAILS ✗ u_cpu.u_alu.carry_internal → CUVUNF vlsitrainers.com

RTL vs post-synthesis hierarchy. In RTL (left), u_alu is a distinct submodule with its own scope — carry_internal is a named signal within it. After synthesis flattening (right), u_alu no longer exists as a separate scope. Its logic is merged directly into u_cpu, and internal nets are renamed by the synthesis tool. A testbench reference to u_cpu.u_alu.carry_internal fails with CUVUNF because the path component u_alu does not exist in the netlist.

Resolution Two strategies: update testbench paths or preserve hierarchy in synthesis

Strategy 1 — Update testbench to use post-synthesis net names (immediate fix)

Ask the backend team for the post-synthesis net name that corresponds to the RTL signal. The synthesis tool maintains a mapping between RTL signal names and the gates/nets that implement them. Replace every RTL hierarchical reference in the testbench with the corresponding post-synthesis net path.

// RTL testbench reference (breaks in GLS):
force tb_top.dut.u_cpu.u_alu.carry_internal = 1'b0;

// Updated for post-synthesis netlist (ask backend for net name):
force tb_top.dut.u_cpu.net_carry_347 = 1'b0;

Strategy 2 — Preserve hierarchy in synthesis (prevents the problem)

Ask the synthesis team to preserve the hierarchical boundary during flattening. This is done by applying a preserve attribute to the module before synthesis runs, instructing the tool not to flatten that specific hierarchy level. The syntax varies by synthesis tool but the concept is a hierarchy preservation constraint:

// RTL attribute to preserve hierarchy (tool-agnostic concept):
set_attribute preserve true [get_cells u_alu]

// After synthesis with preserve, the hierarchy is maintained:
// u_cpu.u_alu still exists as a scope in the netlist
// and carry_internal (or its synthesis equivalent) is accessible
Preserve has a timing cost. Preserving hierarchy prevents the synthesis tool from optimising logic across the boundary — cells on both sides of the boundary cannot be merged or restructured together. For large blocks, this can result in a timing penalty of 5–15% on paths that cross the preserved boundary. Use preservation judiciously — only for the specific hierarchical levels that the testbench genuinely needs to access.

📂Issue 3 — FLFFNF: Compiled SDF Not Found

Issue 3 Compiled SDF file specified in command file does not exist FLFFNF

Symptom

During elaboration, the simulator attempts to load the SDF file specified in the SDF command file but cannot find the compiled version. The error reads approximately: “The compiled SDF file specified in the SDF annotation command file does not exist. The annotation command will be ignored.”

Error: FLFFNF — Compiled SDF file 'timing.sdf.X' does not exist.
       The annotation command will be ignored.
// Result: simulation runs with zero delays — timing results meaningless

Root cause

When an SDF command file specifies a COMPILED_SDF_FILE path (pointing to a pre-compiled binary with extension .sdf.X), the simulator expects that binary to exist at the specified location. If the binary was never generated, was generated in a different directory, or the path is wrong, this error occurs. The annotation is silently skipped — the simulation continues but without any delays annotated, which means timing results are completely invalid.

A secondary cause: using COMPILED_SDF_FILE for a file that has been compiled into a different working directory (perhaps by a previous simulation run in another location), and then running the current simulation from a different directory without updating the path.

Resolution Three options: use SDF_FILE, compile manually, or fix the path

Option 1 — Use SDF_FILE instead of COMPILED_SDF_FILE (recommended for new setups)

Replace COMPILED_SDF_FILE with SDF_FILE in the SDF command file. The simulator will automatically compile the text SDF to a binary before annotation — no pre-compiled file is needed. The compiled binary is written to the current working directory.

// Instead of this (may fail if .X file doesn't exist):
COMPILED_SDF_FILE  "timing.sdf.X"

// Use this (auto-compiles if needed):
SDF_FILE           "timing.sdf"
SCOPE              tb_top.dut
MTM_CONTROL        MAXIMUM

Option 2 — Pre-compile the SDF manually

For large SDF files (common in post-P&R full-chip runs), pre-compiling once and reusing the binary saves significant time across repeated simulation runs. The compilation command converts the text SDF to a binary format:

// Compile the SDF text file to binary (run once):
sdf_compile timing.sdf
// Generates: timing.sdf.X in the current directory

// Then in the SDF command file, use absolute path:
COMPILED_SDF_FILE  "/project/gls/sdf/timing.sdf.X"

Option 3 — Fix the path in the SDF command file

If the binary exists but is in a different location, update the COMPILED_SDF_FILE path to the correct absolute path. Using absolute paths rather than relative paths in SDF command files prevents this class of error entirely — the file is found regardless of which directory the simulation is launched from.

Best practice: Use SDF_FILE (text, auto-compiled) for development runs where the SDF changes frequently with each new netlist delivery. Switch to COMPILED_SDF_FILE with an absolute path for regression runs where the same SDF is used across hundreds of test cases — the one-time compilation cost is amortised across all runs.

⚠️Issue 4 — SDFNEP and SDFNET Annotation Warnings

Issue 4 SDF path or timing check does not exist in the HDL cell model SDFNEP · SDFNET

Symptoms

During SDF annotation at elaboration, the simulator issues warnings for arcs or timing checks in the SDF that could not be matched to the cell model’s specify block:

Warning: SDFNEP — This path, including the condition if any, does not exist
         in the instance being annotated. The requested annotation will not
         occur. Instance: tb_top.dut.u_logic.g7, Cell: NAND2BX1
         Path: (IOPATH (posedge A) Y)

Warning: SDFNET — This timing check, including conditions if any, does not
         exist in the instance being annotated.
         Instance: tb_top.dut.u_ff.reg0, Cell: DFFX1
         Check: (SETUPHOLD (posedge D) (posedge CK))

Root cause — SDFNEP (path does not exist)

SDFNEP fires when the SDF specifies an IOPATH arc that does not exist in the cell’s specify block. Common causes:

  • The SDF was generated from a different version of the standard cell library than the simulation model being used
  • The SDF contains a conditional path (with an edge specifier like posedge A) but the cell model’s specify block has a non-conditional path (without an edge specifier)
  • The cell type in the netlist was replaced or renamed during ECO and the SDF was not regenerated

Root cause — SDFNET (timing check does not exist)

SDFNET fires when the SDF specifies a timing check construct that cannot be matched to a corresponding check in the cell model’s specify block. The most common cause is a format mismatch between how setup/hold is expressed:

Figure 4 — SDF annotation matching rules: when annotation succeeds and when it fails
SDF construct HDL model specify block Result (IOPATH (posedge A) Y …) (posedge A) => Y : … ; ✓ Annotated — exact match (IOPATH (posedge A) Y …) A => Y : … ; (no edge) ✗ SDFNEP — edge mismatch (IOPATH A Y …) (no edge) any A=>Y path (any edge) ✓ Annotated — overwrites all (SETUPHOLD D (posedge CK)) $setuphold(CK, D, …) ✓ Annotated — direct match (SETUPHOLD D (posedge CK)) $setup(D,CK,…) $hold(CK,D,…) ⚠ SDFNET — use +splitvlog_suh (HOLD D (posedge CK) …) $setuphold(CK, D, …) [combined] ✗ SDFNET — combined ≠ separate vlsitrainers.com

SDF annotation matching rules. Exact matches (same edge specifiers, same check type) annotate successfully. A conditional IOPATH (with posedge/negedge edge specifier) in the SDF requires an exact match in the model — if the model has a non-conditional path, SDFNEP fires. A non-conditional IOPATH in the SDF will annotate to any matching path in the model, including edge-specified ones, overwriting them. SETUPHOLD in the SDF annotates to $setuphold in the model; to annotate to separate $setup/$hold statements, the +splitvlog_suh option is needed.

Resolution Three approaches based on the specific mismatch type

For SDFNEP — edge specifier mismatch on IOPATH

The SDF contains a conditional path (with posedge or negedge qualifier) but the cell model specify block has only unconditional paths. The fix is to update the cell model’s specify block to add the conditional path, or to request that the SDF be regenerated without the edge qualifiers for that cell type. The preferred fix depends on which is more accurate: if the cell genuinely has edge-dependent timing, the model should be updated; if the SDF edge qualifier is overly specific, the SDF should be simplified.

For SDFNET — SETUPHOLD in SDF vs separate $setup/$hold in model

Use the +sdf_splitvlog_suh annotation option, which instructs the annotator to split each SETUPHOLD entry in the SDF into separate $setup and $hold components for annotation. This works provided neither the setup nor hold value in the SDF is negative — if either value is negative (which occurs in advanced-node cells with NTC), splitting is not valid and the model must be updated instead.

// Add this option to the SDF command file for SETUPHOLD→$setup/$hold split:
SDF_FILE     "timing.sdf"
SCOPE        tb_top.dut
MTM_CONTROL  MAXIMUM
+sdf_splitvlog_suh    // split SETUPHOLD into $setup + $hold

Evaluating whether SDFNEP/SDFNET warnings are critical

Not every SDFNEP or SDFNET warning is a problem that needs immediate action. The triage framework:

Warning patternSeverityAction
SDFNEP on a handful of exotic cell types Medium Investigate whether those cells are on timing-critical paths. If not critical, waive with documentation.
SDFNEP on all instances of a common cell (e.g. all DFFX1) High Systematic library/SDF mismatch. Fix the cell model or request updated SDF. Do not waive.
SDFNET on $hold where library uses $setuphold Medium — expected Confirm $setuphold annotates at 100%. The individual $hold warnings are a format mismatch, not a coverage gap. Document and waive.
SDFNET on all $setuphold — 0% timing check coverage Critical No timing checks are active. Root cause is a fundamental library model mismatch. Do not simulate for signoff until resolved.
SDFNEP and SDFNET do not cause simulation to stop. The affected timing arcs simply are not annotated — they retain the default values from the specify block, or 0 if the specify block has no default. This means a design with significant SDFNEP warnings is being simulated with incorrect delays on those paths. Whether this matters depends on whether those paths are timing-critical. Always resolve systematic warnings before treating GLS results as valid for signoff.

🔬VLSI Connections

🔬 Zero-delay loops and scan chain design

The most common source of zero-delay loops in production GLS is the UDP (User Defined Primitive) models used for sequential elements — flip-flops modelled with Verilog UDPs have internal feedback by definition. In RTL simulation, this is harmless because the flip-flop model is behavioural. In gate-level simulation, the structural UDP model with zero delay can create a loop if the output feeds back to the input at the same simulation time. The standard solution is the -seq_udp_delay switch, which adds a uniform small delay to all sequential UDP outputs. The value should be smaller than any real clock period in the design so it does not perturb timing, but large enough to break the simultaneous event cycle. For a design with a 1 ns minimum clock period, a seq_udp_delay of 0.01 ns (10 ps) is typically used. This is not a timing-accurate fix — it is a simulation workaround specific to zero-delay mode. In SDF mode, the real cell delays replace the UDP delay and the loop ceases to be a problem.

🔬 Synthesis hierarchy preservation strategy at the block level

The CUVUNF error is one of the most common first-time GLS failures, and its prevention requires coordination between verification and RTL/synthesis teams before synthesis runs — not after. The most effective approach is a “GLS readiness review” before the first synthesis run: the verification team lists every hierarchical signal path accessed in the testbench (for forcing, probing, or coverage collection), and the synthesis team maps each one to either a preserved hierarchy path or a post-synthesis net name alternative. Paths that would require preservation of large sub-blocks (and incur significant timing penalties) are often better replaced with testbench probe nets at the DUT boundary rather than deep internal nodes. This negotiation prevents the situation where a late-project GLS bring-up is blocked for days by CUVUNF errors on fifty different testbench references.

🔬 SDFNEP as a library qualification indicator

A high rate of SDFNEP warnings is sometimes the first indicator that the standard cell library simulation model is out of sync with the liberty (.lib) file used for STA. In a well-maintained PDK, the liberty file and the Verilog simulation model are generated from the same characterisation database and contain identical timing arcs. In practice, IP vendors and foundry PDKs occasionally ship with minor discrepancies — a new cell variant characterised for the liberty file but not yet added to the simulation model, or an edge-conditional arc added to the liberty but expressed differently in the Verilog model. When SDFNEP warnings fire systematically on specific cell types, the correct first step is to compare the IOPATH arcs in the SDF (derived from liberty) against the conditional path statements in the Verilog model’s specify block. Discrepancies should be reported to the library vendor for the next PDK update and documented as waivers for the current tape-out.

Summary — GLS-06 key points: Issue 1 (TRZDGOC — zero-delay gate oscillation): combinational feedback loop with no delay spins infinitely at a single simulation time; detect with -gateloopwarn -access +rwc; identify with drivers -active TCL command; resolve by adding seq_udp_delay, switching to unit delay, or fixing the design. Issue 2 (CUVUNF — hierarchy not found): synthesis flattening dissolves RTL submodule boundaries, breaking testbench hierarchical references; resolve by updating testbench to post-synthesis net names (immediate) or asking synthesis to preserve the hierarchy (preferred, with timing cost caution). Issue 3 (FLFFNF — compiled SDF not found): SDF command file points to a non-existent .sdf.X binary; resolve by using SDF_FILE (auto-compile) instead of COMPILED_SDF_FILE, or pre-compiling with the SDF compiler tool and using an absolute path. Issue 4 (SDFNEP/SDFNET — path or check not found): edge specifier mismatch (SDFNEP), or SETUPHOLD vs $setup/$hold format mismatch (SDFNET); resolve SDFNET with +sdf_splitvlog_suh if no negative values; systematic warnings on common cells indicate library/SDF mismatch requiring library update; sporadic warnings may be waived with documentation.
Scroll to Top