IC Backend Execution Specialists
May 1, 20269 min read
Signoff Methodology

PrimeTime Writes Wrong Conditional WIDTH in SDF? The Parallel Constraint Arcs Trap

When a pin carries two parallel min_pulse_width constraints with different sdf_cond — like an eFuse STROBE in read vs program mode — PrimeTime writes both COND WIDTH lines with the same merged (min::max) values. The fix is one variable, but it has to be set before link_design, which trips up everyone who relies on restore_session.

Context

01Where This Comes From

An eFuse hard macro on a TSMC 28nm SoC. The vendor library defines two min_pulse_width constraints on the STROBE pin — one for read mode (~142 ns) and one for program mode (~11000 ns) — distinguished only by sdf_cond strings such as check_read_start and check_pgm_start. After full PrimeTime signoff, the SDF written for gate-level simulation contained two COND WIDTH lines on STROBE, and both reported the same (min::max) pair: 142.4570 :: 11000.0000.

That output looks plausible at first glance — the values are right. But it is silently wrong: the two values do not belong to the same condition. PrimeTime has merged the read-mode pulse-width value with the program-mode pulse-width value into a single (min::max) bounding pair, and then stamped that pair on every COND WIDTH line for the same pin.

When the gate-level simulator picks up that SDF, every WIDTH check on STROBE — whether the design is in read or program mode — will be evaluated against the wrong upper bound. False violations during functional simulation, real ones potentially masked. Either way, the SDF is no longer a faithful reflection of the library.

02The Symptom

Two parallel min_pulse_width entries in the lib become two COND WIDTH lines in the SDF, but the numeric values are mixed across conditions instead of staying per-condition.

What PrimeTime Writes
(WIDTH (COND check_read_start (posedge STROBE)) (142.4570::11000.0000)) and the same (142.4570::11000.0000) on the check_pgm_start line. Read-mode and program-mode both inherit the merged bounding pair.
What the Library Says
min_pulse_width with when : '!PD*!CSB*!PGENB*LOAD*!PS' has constraint_high : 142.45701. The other when : '!PD*!CSB*!PGENB*!LOAD*PS' has constraint_high : 11000.00002. Two distinct constraints, each tied to a mutually-exclusive functional state.
What the SDF Should Look Like
Each COND line should carry only its own value: (142.457::142.457) for check_read_start and (11000.000::11000.000) for check_pgm_start. The min and max sides of the pair are the corner range of one condition, never a mix of two.
Why It Matters
Gate-level simulation reads SDF literally. A merged (min::max) on a COND line is not a corner pair — it is a fabrication. WIDTH violations either fire spuriously during program-mode simulation reading the read-mode line, or get masked because the bound is too loose.
Liberty

03What Liberty Actually Encodes

Two parallel min_pulse_width constraints with mutually-exclusive when expressions — a standard pattern for IPs with mode pins.

  • Mode Pins Drive the Conditions

    PD, CSB, PGENB, LOAD, PS — five mode pins on the eFuse. The when string of each min_pulse_width describes a unique functional state.

  • sdf_cond Is the Output Tag

    Each min_pulse_width carries an sdf_cond string that the SDF writer is supposed to emit verbatim as the COND clause. It is the only handle the SDF has on which functional state the value belongs to.

  • Mutual Exclusivity Is a Lib Rule

    Library Compiler will warn if the when conditions of parallel min_pulse_width entries are not mutually exclusive. If the lib is clean, only one constraint is ever active in any real silicon state.

  • constraint_high Is the High-Pulse Width

    min_pulse_width () { constraint_high : N; } sets the minimum required width of the high pulse. The two constraints in this case differ by ~80x — read-mode 142 ns vs program-mode 11 us.

04Root Cause: Parallel Constraint Arcs Are Merged at Link Time

PrimeTime, by default, stores parallel constraint arcs in a bounding form. Two min_pulse_width values on the same endpoint do not survive link_design as two distinct values — they collapse into a single (min, max) pair stored on the bundled arc. write_sdf then emits the two COND lines from the same merged pair.

Bounding Merge Is an Optimization
Modern PrimeTime treats parallel constraint arcs as one analysis object with bounding values. For most STA work this is faster and produces equally pessimistic results — the worst constraint dominates.
It Is Lossy for SDF
Once the two distinct values become a single bounding pair, the per-condition information is gone. The COND tags survive on the arc, but each tag now points at the same numeric pair.
Older PrimeTime Did Not Do This
The Synopsys reference describes the alternative behavior as 'matches the default behavior of older PrimeTime releases' — the merge was added as an optimization, with a compatibility switch to opt out.
It Is Decided at Link Time
Whether merging happens is decided when link_design builds the timing graph and freezes how parallel arcs are stored. After link, the data structure is fixed.
PrimeTime Variable

05The Variable: timing_parallel_constraint_arcs_compatibility

One application variable controls the entire behavior. Default value, false. To get per-condition SDF output, set it to true.

  1. 01
    Default Is false
    PrimeTime ships with timing_parallel_constraint_arcs_compatibility set to false. Parallel constraint arc values are merged into bounding pairs at link time. This is the source of the SDF symptom.
  2. 02
    Set It to true to Disable Merging
    set timing_parallel_constraint_arcs_compatibility true tells PrimeTime to keep all parallel constraint arc values intact. Each sdf_cond keeps its own constraint_high value through link, update_timing, and write_sdf.
  3. 03
    It Has to Be Set Before link_design
    The Synopsys man page is explicit: 'This variable must be set before the link_design command is run.' link_design is when parallel arc storage is decided. Setting the variable later changes nothing.
  4. 04
    The Compatibility Hint
    The variable name and description both signal that the non-merged behavior used to be the default. If a legacy script wrote correct SDF without ever setting this variable, it almost certainly ran on an older PrimeTime where merge was off by default.

06The Timing Trap: restore_session Does Not Help

The single most common way teams burn an afternoon on this issue is setting the variable in the wrong place. PrimeTime sessions store the linked timing graph, not the application variable state, so any variable that influences linking has to be set in the session that originally ran link_design — not in a session that restores the result.

restore_session Brings Back a Linked Database
save_session captures the post-link timing graph. restore_session re-loads it. By the time you have a restored session, link_design has already happened — with whatever value of timing_parallel_constraint_arcs_compatibility was active in the originating run.
Setting It After restore_session Is a No-Op for SDF
You can echo the variable, you can printvar it, you can call write_sdf — none of it re-derives the bounding decision. The arcs are already stored merged.
Resave Does Not Fix It Either
Setting the variable, then save_session-ing the already-merged graph and restoring it later, freezes the wrong state into the new session file. The fix has to come before link_design.
DMSA / Multi-Scenario Workers Need It Too
In distributed flows, every scenario worker runs its own link_design. The variable must be present in each worker's startup before link — either in a common setup script sourced by all scenarios or distributed via set_distributed_variables before scenario activation.
Cross-Tool Behavior

07PT vs Tempus: A Related Conditional Pulse-Width Difference

The same eFuse setup also produces a violation in PrimeTime that Cadence Tempus does not flag. Different issue, same root structure: how each tool treats conditional min_pulse_width constraints when no explicit case_analysis is given.

On the same STROBE pin, PrimeTime reports a min_pulse_width violation against the program-mode requirement of 11000 ns, even when the SoC is in functional read mode and the actual STROBE pulse is ~114 ns. Slack reports as -10886 ns. Tempus, on the same database, reports nothing.

PrimeTime does not symbolically evaluate the when expression of a conditional min_pulse_width. It cannot tell that PD, CSB, PGENB, LOAD, PS are constrained into a state that makes check_pgm_start unreachable. Without an explicit case_analysis on those mode pins, both conditional constraints stay active, and the worst one drives the violation.

Tempus appears to apply a different default for unresolved conditional pulse-width checks — closer to 'do not report unless the condition is reachable.' Neither behavior is incorrect; they are different defaults for the same ambiguous input. In PrimeTime, the fix is to lock the mode pins with set_case_analysis so the program-mode constraint is automatically deactivated, or to disable the program-mode arc directly with set_disable_timing on the corresponding timing arc.

08The Fix Recipe

A clean, repeatable PrimeTime flow that produces per-condition SDF and avoids the spurious mode-conditional violations. Treat this as a checklist for any IP that uses parallel conditional constraints — eFuse, OTP, custom analog wrappers, anything with mode-dependent min_pulse_width or min_period.

Set Compatibility Variables First
Before any read or link command: set timing_parallel_constraint_arcs_compatibility true and set sdf_enable_cond_start_end true. The latter ensures the SDF writer honors any sdf_cond_start / sdf_cond_end attributes the lib provides.
Read and Link in the Same Session
Read libraries, read netlist, link_design — all in one shell. Do not start from a saved session unless you are certain that session was created with the variable already true.
Apply Mode Case Analysis
For functional scenarios, fix the mode pins so only one conditional constraint stays active per scenario. set_case_analysis 0 on PD, CSB, PS; set_case_analysis 1 on LOAD, PGENB. Validate with report_case_analysis.
Write SDF in Version 3.0
write_sdf -version 3.0 out.sdf. SDF 2.1 has limited support for conditional WIDTH constructs — version 3.0 is the only safe choice when sdf_cond matters.
Save the Session After write_sdf
If you need a restorable checkpoint, save_session at the end. Future restore sessions will inherit the correctly stored arcs. Document in the project README that this session was built with the compatibility variable enabled.
Verification

09How to Verify the Output Is Correct

Three checks confirm that the fix landed and the SDF is faithful.

  • 1. Confirm the Variable Is Active Before link_design

    printvar timing_parallel_constraint_arcs_compatibility right after set, and again immediately before link_design. Use report_app_var -only_non_default to spot-check the full set of overrides.

  • 2. Verify sdf_cond Survived in the Library

    After link, query: foreach_in_collection a [get_lib_timing_arcs -of [get_lib_pins '*/STROBE']] { echo "[get_attribute $a sdf_cond] [get_attribute $a when]" }. Empty sdf_cond fields mean the .db was compiled without preserving the strings — recompile from the original .lib.

  • 3. Diff the SDF Per-Condition

    After write_sdf, grep for the pin and confirm each COND WIDTH line carries (X::X) where X matches the constraint_high of that specific when. If two lines on the same pin still share an identical (min::max) pair, the merge happened — go back and check that the variable was set before link.

10What Else Can Affect This

The variable does not operate in isolation. A few neighboring settings either reinforce, mask, or override its effect.

timing_reduce_parallel_cell_arcs Is Different
Easy to confuse. timing_reduce_parallel_cell_arcs governs parallel cell delay arcs (default true), not constraint arcs. Tuning it does not change conditional WIDTH SDF output. Advanced waveform mode auto-flips it to false and emits PTE-112 — which is unrelated to the conditional constraint trap.
set_min_pulse_width Overrides Liberty
User-supplied set_min_pulse_width on a pin replaces all library-side conditional constraints for that pin. The compatibility variable then has nothing to keep separate. Useful as an override, dangerous as an unintentional mask of a real program-mode requirement.
set_disable_timing on the Wrong Mode Arc
A surgical alternative to case_analysis: directly disable the inactive conditional timing arc with set_disable_timing on the matching get_timing_arcs collection. Confirm with report_disable_timing that only the program-mode constraint arc is gone.
Lib Compile Quality
If the .db was compiled by a stripped-down Library Compiler that did not preserve sdf_cond, no PrimeTime variable can recover it. Always re-compile from the original .lib if the lib_timing_arc query returns empty sdf_cond strings.

11Why This Pattern Repeats Across Projects

Conditional min_pulse_width and min_period constraints appear in any IP with mode pins — eFuse, OTP, MTP, PLLs with mode controls, certain ADC sample clocks. The library encoding is standard. The signoff failure modes are also standard.

Default-Off Compatibility Switches Are Easy to Miss
Variables that change a numerically-significant behavior but default to the modern, faster path are the most common source of cross-project drift. Pin them in a project-wide setup script.
Session Restore Hides the Order of Operations
save_session is a productivity feature for engineers iterating on reports — it freezes a database. Anything that influences how that database is built must run upstream of the save, not the restore.
Cross-Tool Defaults Are Not Bugs
PrimeTime and Tempus disagree on conditional pulse-width handling without explicit case_analysis. Treat the disagreement as a request from both tools for clearer mode constraints, not as a tool quality difference.
Gate-Level Sim Trusts SDF Literally
Gate-level simulators apply the numbers in the SDF without sanity-checking them against the lib. A merged COND value is invisible to simulation but produces hours of false debugging downstream.

12Closing Note

Most of the engineering risk in modern signoff is no longer in the math; it is in what the tools assume by default and how those assumptions interact with library encoding. The conditional min_pulse_width / SDF case is a clean example: one variable, one ordering rule, one library convention — and a failure mode that survives full STA signoff and lands in gate-level simulation as silent corruption.

If your project uses eFuse, OTP, or any IP with mode-dependent pulse-width constraints, audit the SDF you hand off to gate-level simulation. Look for COND WIDTH lines on the same pin sharing identical (min::max) values — that is the fingerprint of this trap, and it is fixable in one line of TCL placed in the right file.

Hitting an SDF or signoff anomaly you cannot explain?

We have spent more time than we would like inside PrimeTime variables, lib compilation, and tool default differences. Send the symptom — we will tell you whether it is a setup issue, a tool bug, or something the lib is hiding.

References

  1. [1]
  2. [2]
  3. [3]