From b0f4954774569ae3f72e1b5ca612861533897a15 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Tue, 11 Aug 2026 20:42:47 +0000 Subject: [PATCH 1/3] add bug reproducer Signed-off-by: dsengupta0628 --- dcalc/test/CMakeLists.txt | 1 + dcalc/test/dcalc_case_analysis_leak.ok | 10 +++++ dcalc/test/dcalc_case_analysis_leak.tcl | 59 +++++++++++++++++++++++++ dcalc/test/dcalc_case_analysis_leak.v | 12 +++++ 4 files changed, 82 insertions(+) create mode 100644 dcalc/test/dcalc_case_analysis_leak.ok create mode 100644 dcalc/test/dcalc_case_analysis_leak.tcl create mode 100644 dcalc/test/dcalc_case_analysis_leak.v diff --git a/dcalc/test/CMakeLists.txt b/dcalc/test/CMakeLists.txt index 6caa2a23..b572e07c 100644 --- a/dcalc/test/CMakeLists.txt +++ b/dcalc/test/CMakeLists.txt @@ -1,5 +1,6 @@ sta_module_tests("dcalc" TESTS + case_analysis_leak prima_report ) diff --git a/dcalc/test/dcalc_case_analysis_leak.ok b/dcalc/test/dcalc_case_analysis_leak.ok new file mode 100644 index 00000000..efb775b2 --- /dev/null +++ b/dcalc/test/dcalc_case_analysis_leak.ok @@ -0,0 +1,10 @@ + +load on constant net cn (ff) arrival at z (ps) +0 159.19 +50 159.71 +200 167.60 +500 181.63 +1000 205.57 + +arrival spread across the sweep: 46.38 ps (expected 0.00) + diff --git a/dcalc/test/dcalc_case_analysis_leak.tcl b/dcalc/test/dcalc_case_analysis_leak.tcl new file mode 100644 index 00000000..47cd4dca --- /dev/null +++ b/dcalc/test/dcalc_case_analysis_leak.tcl @@ -0,0 +1,59 @@ +# Delay calculation ignores set_case_analysis constants. +# +# GraphDelayCalc uses DcalcPred (dcalc/GraphDelayCalc.cc), whose searchFrom +# stops only at power/ground nets: +# +# return !(sdc->isDisabledConstraint(from_pin) +# || (net && (network->isPower(net) || network->isGround(net)))); +# +# SearchPred0::searchFrom (search/SearchPred.cc) instead stops at +# sim->isConstant(from_vertex). DcalcPred::searchTo returns true +# unconditionally where SearchPred0::searchTo returns !sim->isConstant, and +# DcalcPred::searchThru omits sim->isDisabledCond and the +# simTimingSense == none test. GraphDelayCalc.cc never references Sim at all. +# +# Consequence: in findDriverEdgeDelays (GraphDelayCalc.cc:1011) an arc whose +# source pin is an SDC constant still passes searchFrom/searchThru, so its +# delay and slew are computed and merged into the driver vertex -- including +# when that driver is a live, non-constant pin. +# +# Circuit (dcalc_case_analysis_leak.v): +# +# c --BUF u0--> cn ---A2\ +# AND2 u1 --> n1 --BUF u2--> z +# a -------------------A1/ +# +# set_case_analysis 1 c makes net cn constant. 1 is non-controlling for an +# AND, so n1 and z stay live and the A2->ZN arc is dead. The reported path is +# a -> u1 -> u2 -> z, which never traverses cn. +# +# Sweeping the wire load on cn must not change anything on that path. It +# does: cn's slew rides the dead A2->ZN arc into n1, and n1's slew sets u2's +# delay. The arrival column below should be flat and is not. + +read_liberty ../../examples/nangate45_slow.lib.gz +read_verilog dcalc_case_analysis_leak.v +link_design top + +create_clock -name clk -period 10 +set_input_delay 0 -clock clk [get_ports {a c}] +set_output_delay 0 -clock clk [get_ports z] +set_input_transition 0.010 [get_ports {a c}] + +set_case_analysis 1 [get_ports c] + +puts "" +puts "load on constant net cn (ff) arrival at z (ps)" +set arrivals {} +foreach load {0 50 200 500 1000} { + set_load $load [get_nets cn] + set path [lindex [find_timing_paths -path_delay max] 0] + set arrival [format %.2f [expr [$path data_arrival_time] * 1e12]] + lappend arrivals $arrival + puts [format "%-32s%s" $load $arrival] +} + +set delta [expr [lindex $arrivals end] - [lindex $arrivals 0]] +puts "" +puts "arrival spread across the sweep: [format %.2f $delta] ps (expected 0.00)" +puts "" diff --git a/dcalc/test/dcalc_case_analysis_leak.v b/dcalc/test/dcalc_case_analysis_leak.v new file mode 100644 index 00000000..a55c2dc2 --- /dev/null +++ b/dcalc/test/dcalc_case_analysis_leak.v @@ -0,0 +1,12 @@ +module top (a, c, z); + input a, c; + output z; + wire cn, n1; + + // set_case_analysis 1 on port c makes net cn constant. + BUF_X1 u0 (.A(c), .Z(cn)); + // 1 is non-controlling for AND2, so n1 is NOT constant. + AND2_X1 u1 (.A1(a), .A2(cn), .ZN(n1)); + // Consumes n1's slew, so slew corruption becomes a delay change. + BUF_X1 u2 (.A(n1), .Z(z)); +endmodule From ce8cdadc09c1ab37120e887414c1fd52023c3acd Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 11 Aug 2026 20:54:49 +0100 Subject: [PATCH 2/3] dcalc no slew merge for constant disabled arcs Signed-off-by: James Cherry (cherry picked from commit 8b511bc01261fc5d103d053be45c6a22572475d5) --- dcalc/GraphDelayCalc.cc | 3 +++ search/Sta.cc | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 8dbcfe47..b8b2423e 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -47,6 +47,7 @@ #include "Sdc.hh" #include "Scene.hh" #include "SearchPred.hh" +#include "search/Sim.hh" #include "Stats.hh" #include "TimingArc.hh" #include "TimingRole.hh" @@ -88,8 +89,10 @@ DcalcPred::searchFrom(const Vertex *from_vertex, const Pin *from_pin = from_vertex->pin(); const Sdc *sdc = mode->sdc(); const Network *network = sta_->network(); + const Sim *sim = mode->sim(); Net *net = network->net(from_pin); return !(sdc->isDisabledConstraint(from_pin) + || sim->isConstant(from_vertex) || (net && (network->isPower(net) || network->isGround(net)))); } diff --git a/search/Sta.cc b/search/Sta.cc index a31721a8..36d3eb0b 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -191,6 +191,7 @@ StaSimObserver::StaSimObserver(StaState *sta) : void StaSimObserver::valueChangeAfter(const Pin *pin) { + graph_delay_calc_->delayInvalid(pin); Vertex *vertex = graph_->pinDrvrVertex(pin); if (vertex) { search_->arrivalInvalid(vertex); @@ -3647,8 +3648,10 @@ void Sta::delayCalcPreamble() { ensureLevelized(); - for (Mode *mode : modes_) + for (Mode *mode : modes_) { + mode->sim()->ensureConstantsPropagated(); mode->clkNetwork()->ensureClkNetwork(); + } } void From 5500bdba477b31c86327d87f9405c7b0e3489251 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Tue, 11 Aug 2026 21:34:41 +0000 Subject: [PATCH 3/3] regolden tests for constant disabled arc slew fix Update goldens for "dcalc no slew merge for constant disabled arcs". dcalc_case_analysis_leak: arrival spread across the wire-load sweep on the constant net drops from 46.38 ps to 0.00 ps, which is the point of the test. Rewrite the header comment to describe the fix rather than the open bug. graph_modify: with set_case_analysis 0 on d4, inv2 drives n4=1 and or1 drives n6=1, so nand1/A2 is constant. 1 is non-controlling for a NAND, so nand1/ZN stays live and the A2->ZN arc is dead. Its slew no longer merges into nand1/ZN, so buf4/Z falls from 0.07 to 0.06. The following report, after unset_case_analysis, still shows 0.07 as expected. sdc_port_delay_advanced: in1=0, in2=1 and case analysis 0 on in3 make nor1/ZN constant, so it is no longer reported as the worst max slew pin; reg1/QN takes its place. Signed-off-by: dsengupta0628 --- dcalc/test/dcalc_case_analysis_leak.ok | 12 +++++----- dcalc/test/dcalc_case_analysis_leak.tcl | 32 +++++++++++++------------ graph/test/graph_modify.ok | 2 +- sdc/test/sdc_port_delay_advanced.ok | 2 +- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/dcalc/test/dcalc_case_analysis_leak.ok b/dcalc/test/dcalc_case_analysis_leak.ok index efb775b2..2cc2edc9 100644 --- a/dcalc/test/dcalc_case_analysis_leak.ok +++ b/dcalc/test/dcalc_case_analysis_leak.ok @@ -1,10 +1,10 @@ load on constant net cn (ff) arrival at z (ps) -0 159.19 -50 159.71 -200 167.60 -500 181.63 -1000 205.57 +0 158.79 +50 158.79 +200 158.79 +500 158.79 +1000 158.79 -arrival spread across the sweep: 46.38 ps (expected 0.00) +arrival spread across the sweep: 0.00 ps (expected 0.00) diff --git a/dcalc/test/dcalc_case_analysis_leak.tcl b/dcalc/test/dcalc_case_analysis_leak.tcl index 47cd4dca..ac814f6b 100644 --- a/dcalc/test/dcalc_case_analysis_leak.tcl +++ b/dcalc/test/dcalc_case_analysis_leak.tcl @@ -1,21 +1,22 @@ -# Delay calculation ignores set_case_analysis constants. +# Delay calculation must not merge slew from set_case_analysis constants. # -# GraphDelayCalc uses DcalcPred (dcalc/GraphDelayCalc.cc), whose searchFrom -# stops only at power/ground nets: +# Regression guard for "dcalc no slew merge for constant disabled arcs". +# +# GraphDelayCalc uses DcalcPred (dcalc/GraphDelayCalc.cc). Its searchFrom +# used to stop only at power/ground nets: # # return !(sdc->isDisabledConstraint(from_pin) # || (net && (network->isPower(net) || network->isGround(net)))); # -# SearchPred0::searchFrom (search/SearchPred.cc) instead stops at -# sim->isConstant(from_vertex). DcalcPred::searchTo returns true -# unconditionally where SearchPred0::searchTo returns !sim->isConstant, and -# DcalcPred::searchThru omits sim->isDisabledCond and the -# simTimingSense == none test. GraphDelayCalc.cc never references Sim at all. +# SearchPred0::searchFrom (search/SearchPred.cc) stops at +# sim->isConstant(from_vertex); DcalcPred did not, so in +# findDriverEdgeDelays an arc whose source pin is an SDC constant still +# passed searchFrom/searchThru and had its delay and slew merged into the +# driver vertex -- including when that driver is a live, non-constant pin. # -# Consequence: in findDriverEdgeDelays (GraphDelayCalc.cc:1011) an arc whose -# source pin is an SDC constant still passes searchFrom/searchThru, so its -# delay and slew are computed and merged into the driver vertex -- including -# when that driver is a live, non-constant pin. +# The fix adds sim->isConstant(from_vertex) to DcalcPred::searchFrom, and +# makes Sta::delayCalcPreamble propagate constants before delay calculation +# so the predicate sees them. # # Circuit (dcalc_case_analysis_leak.v): # @@ -27,9 +28,10 @@ # AND, so n1 and z stay live and the A2->ZN arc is dead. The reported path is # a -> u1 -> u2 -> z, which never traverses cn. # -# Sweeping the wire load on cn must not change anything on that path. It -# does: cn's slew rides the dead A2->ZN arc into n1, and n1's slew sets u2's -# delay. The arrival column below should be flat and is not. +# Sweeping the wire load on cn must not change anything on that path. Before +# the fix it did: cn's slew rode the dead A2->ZN arc into n1, and n1's slew +# set u2's delay, so the arrival spread was 46.38 ps. The arrival column +# below must now be flat. read_liberty ../../examples/nangate45_slow.lib.gz read_verilog dcalc_case_analysis_leak.v diff --git a/graph/test/graph_modify.ok b/graph/test/graph_modify.ok index d1d2c152..72bdbb69 100644 --- a/graph/test/graph_modify.ok +++ b/graph/test/graph_modify.ok @@ -2415,7 +2415,7 @@ Corner: slow 0.14 1.14 v buf1/Z (BUF_X1) 0.09 1.23 v and1/ZN (AND2_X1) 0.09 1.32 ^ nand1/ZN (NAND2_X1) - 0.07 1.38 ^ buf4/Z (BUF_X4) + 0.06 1.38 ^ buf4/Z (BUF_X4) 0.00 1.38 ^ q3 (out) 1.38 data arrival time diff --git a/sdc/test/sdc_port_delay_advanced.ok b/sdc/test/sdc_port_delay_advanced.ok index 32e0b4a4..8ee4dbd5 100644 --- a/sdc/test/sdc_port_delay_advanced.ok +++ b/sdc/test/sdc_port_delay_advanced.ok @@ -111,7 +111,7 @@ max slew Pin Limit Slew Slack ------------------------------------------------------------ -nor1/ZN 0.20 0.01 0.18 (MET) +reg1/QN 0.20 0.01 0.19 (MET) max fanout