diff --git a/src/dbSta/include/db_sta/dbNetwork.hh b/src/dbSta/include/db_sta/dbNetwork.hh index 96aaeb4c865..190b53d0642 100644 --- a/src/dbSta/include/db_sta/dbNetwork.hh +++ b/src/dbSta/include/db_sta/dbNetwork.hh @@ -367,7 +367,10 @@ class dbNetwork : public ConcreteNetwork // Return the highest net above the given net. // - If the net is a flat net, return it. - // - If the net is a hier net, return the modnet in the highest hierarchy. + // - If the net is a hier net (dbModNet), return the associated flat dbNet. + // This ensures parasitic externality checks in ensureParasiticNode work + // correctly: net_ is always a flat net, so the comparison net != net_ + // must also operate on flat nets. Net* highestNetAbove(Net* net) const override; //////////////////////////////////////////////////////////////// diff --git a/src/dbSta/src/dbNetwork.cc b/src/dbSta/src/dbNetwork.cc index 49514f4a413..9237290c88e 100644 --- a/src/dbSta/src/dbNetwork.cc +++ b/src/dbSta/src/dbNetwork.cc @@ -2018,9 +2018,18 @@ void dbNetwork::visitConnectedPins(const Net* net, } } +// Caution: +//- Network::highestConnectedNet(Net *net) retrieves the highest hierarchical +// net connected to the given net. +// - But `dbNetwork::highestConnectedNet(Net* net)` retrieves the corresponding +// flat net for the given net. +// - It behaves differently to cope with the issue 9724. +// - This redefinition may cause another issue later when +// `Network::highestConnectedNet(Net *net)` is used elsewhere. const Net* dbNetwork::highestConnectedNet(Net* net) const { - return net; + const Net* flat = findFlatNet(net); + return flat ? flat : net; } //////////////////////////////////////////////////////////////// @@ -5218,11 +5227,15 @@ Net* dbNetwork::highestNetAbove(Net* net) const } if (modnet) { + // Return the flat net associated with this mod net. + // Parasitic externality checks in + // ConcreteParasiticNetwork::ensureParasiticNode compare against net_ which + // is always a flat net (set via makeParasiticNetwork). Returning the + // highest mod net causes all pin nodes on hierarchically-connected nets to + // compare unequal to net_ and be incorrectly marked as external, making + // node_count_ = 0 and crashing PRIMA in measureThresholds. if (dbNet* related_dbnet = modnet->findRelatedNet()) { - if (odb::dbModNet* highest_modnet - = related_dbnet->findModNetInHighestHier()) { - return dbToSta(highest_modnet); // Found the highest modnet - } + return dbToSta(related_dbnet); } } diff --git a/src/dbSta/test/BUILD b/src/dbSta/test/BUILD index 7f92bb385fc..2777fa257e6 100644 --- a/src/dbSta/test/BUILD +++ b/src/dbSta/test/BUILD @@ -27,6 +27,7 @@ ALL_TESTS = [ "hierclock", "hierwrite", "hier_deep", + "hier_highest_connected_net", "hier_weird_port", "levelize_drvr_vertices1", "make_port", diff --git a/src/dbSta/test/CMakeLists.txt b/src/dbSta/test/CMakeLists.txt index b9d63de611b..66c31a6e138 100644 --- a/src/dbSta/test/CMakeLists.txt +++ b/src/dbSta/test/CMakeLists.txt @@ -17,6 +17,7 @@ or_integration_tests( hierclock hierwrite hier_deep + hier_highest_connected_net hier_weird_port levelize_drvr_vertices1 make_port diff --git a/src/dbSta/test/cpp/TestDbSta.cc b/src/dbSta/test/cpp/TestDbSta.cc index 1825ff0ad3e..d51382780ea 100644 --- a/src/dbSta/test/cpp/TestDbSta.cc +++ b/src/dbSta/test/cpp/TestDbSta.cc @@ -82,9 +82,26 @@ TEST_F(TestDbSta, TestHierarchyConnectivity) ASSERT_NE(modnet_out2, nullptr); Net* sta_modnet_out2 = db_network_->dbToSta(modnet_out2); ASSERT_NE(sta_modnet_out2, nullptr); + // highestNetAbove on a mod net now returns the associated flat dbNet, not the + // highest mod net, so that parasitic externality checks work correctly. Net* sta_highest_modnet_out = db_network_->highestNetAbove(sta_modnet_mod_out); - ASSERT_EQ(sta_highest_modnet_out, sta_modnet_out2); + ASSERT_EQ(sta_highest_modnet_out, sta_dbnet_out2); + + // Regression for PR #3194: dbNetwork::highestConnectedNet on a hier + // dbModNet must return the associated flat dbNet, not the modnet wrapper + // itself. Parasitics::findParasiticNet uses this as the lookup key into + // the parasitic network map. SPEF is a flat format so parasitics are + // keyed by flat dbNet; returning the modnet wrapper caused a cache miss + // and silently degraded delay calculation to LumpedCap. + const Net* sta_highest_connected_modnet + = db_network_->highestConnectedNet(sta_modnet_mod_out); + ASSERT_EQ(sta_highest_connected_modnet, sta_dbnet_out2); + + // Flat dbNet input: highestConnectedNet is idempotent. + const Net* sta_highest_connected_flat + = db_network_->highestConnectedNet(sta_dbnet_out2); + ASSERT_EQ(sta_highest_connected_flat, sta_dbnet_out2); // Check get_ports -of_object Net* NetTermIterator* term_iter = db_network_->termIterator(sta_dbnet_out2); diff --git a/src/dbSta/test/hier_highest_connected_net.ok b/src/dbSta/test/hier_highest_connected_net.ok new file mode 100644 index 00000000000..bc99f5fb785 --- /dev/null +++ b/src/dbSta/test/hier_highest_connected_net.ok @@ -0,0 +1,7 @@ +[INFO ODB-0227] LEF file: Nangate45/Nangate45.lef, created 22 layers, 27 vias, 135 library cells +[WARNING ORD-0011] Hierarchical flow (-hier) is currently in development and may cause multiple issues. Do not use in production environments. +Found 3 unannotated drivers. + ff_out/QN + ff_top/QN + sub_inst/ff_sub/QN +Found 0 partially unannotated drivers. diff --git a/src/dbSta/test/hier_highest_connected_net.spef b/src/dbSta/test/hier_highest_connected_net.spef new file mode 100644 index 00000000000..9f16f403b40 --- /dev/null +++ b/src/dbSta/test/hier_highest_connected_net.spef @@ -0,0 +1,97 @@ +*SPEF "IEEE 1481-1998" +*DESIGN "top" +*DATE "now" +*VENDOR "n/a" +*PROGRAM "n/a" +*VERSION "1.0" +*DESIGN_FLOW "" +*DIVIDER / +*DELIMITER : +*BUS_DELIMITER [ ] +*T_UNIT 1 NS +*C_UNIT 1 PF +*R_UNIT 1 OHM +*L_UNIT 1 HENRY + +*D_NET clk 0.001 +*CONN +*P clk I +*I ck_buf:A I +*CAP +1 clk 0.0005 +2 ck_buf:A 0.0005 +*RES +1 clk ck_buf:A 0.1 +*END + +*D_NET clk_int 0.002 +*CONN +*I ck_buf:Z O +*I ff_top:CK I +*I ff_out:CK I +*I sub_inst/ff_sub:CK I +*CAP +1 ck_buf:Z 0.0005 +2 ff_top:CK 0.0005 +3 ff_out:CK 0.0005 +4 sub_inst/ff_sub:CK 0.0005 +*RES +1 ck_buf:Z ff_top:CK 0.1 +2 ck_buf:Z ff_out:CK 0.1 +3 ck_buf:Z sub_inst/ff_sub:CK 0.1 +*END + +*D_NET in 0.001 +*CONN +*P in I +*I ff_top:D I +*CAP +1 in 0.0005 +2 ff_top:D 0.0005 +*RES +1 in ff_top:D 0.1 +*END + +*D_NET out 0.001 +*CONN +*I ff_out:Q O +*P out O +*CAP +1 ff_out:Q 0.0005 +2 out 0.0005 +*RES +1 ff_out:Q out 0.1 +*END + +*D_NET midnet 0.001 +*CONN +*I ff_top:Q O +*I sub_inst/ff_sub:D I +*CAP +1 ff_top:Q 0.0005 +2 sub_inst/ff_sub:D 0.0005 +*RES +1 ff_top:Q sub_inst/ff_sub:D 0.1 +*END + +*D_NET intermediate 0.001 +*CONN +*I sub_inst/buf_sub:Z O +*I ff_out:D I +*CAP +1 sub_inst/buf_sub:Z 0.0005 +2 ff_out:D 0.0005 +*RES +1 sub_inst/buf_sub:Z ff_out:D 0.1 +*END + +*D_NET sub_inst/q_int 0.001 +*CONN +*I sub_inst/ff_sub:Q O +*I sub_inst/buf_sub:A I +*CAP +1 sub_inst/ff_sub:Q 0.0005 +2 sub_inst/buf_sub:A 0.0005 +*RES +1 sub_inst/ff_sub:Q sub_inst/buf_sub:A 0.1 +*END diff --git a/src/dbSta/test/hier_highest_connected_net.tcl b/src/dbSta/test/hier_highest_connected_net.tcl new file mode 100644 index 00000000000..3b3611a1e4e --- /dev/null +++ b/src/dbSta/test/hier_highest_connected_net.tcl @@ -0,0 +1,68 @@ +# Regression for PR #3194: dbNetwork::highestConnectedNet on a hier +# dbModNet must resolve to the associated flat dbNet so that +# Parasitics::findParasiticNet hits the parasitic_network_map_ entry +# inserted under the flat dbNet key. +# +# Why this test uses read_spef rather than estimate_parasitics: +# estimate_parasitics keys parasitic_network_map_ via findParasiticNet +# for BOTH insert and lookup, so the bug is self-consistent and +# invisible (insert key == lookup key, both buggy or both fixed). +# SPEF read keys insertion by flat dbNet (parser -> findNet -> flat +# at top scope) while lookup still uses findParasiticNet -> +# dbNetwork::highestConnectedNet. Pre-fix override echoed the modnet +# wrapper -> modnet key != flat key -> cache miss -> driver +# classified unannotated. Fix returns the related flat dbNet -> hit. +# +# Design (hier_highest_connected_net.v): 2-level hier with reg-to-reg +# data path crossing sub_inst. Top clk is buffered by ck_buf so the +# clock-tree driver iterated is a leaf iterm, not the top BTerm; this +# avoids the top-port bypass branch in Parasitics::findParasiticNet +# (which returns network_->net(term) directly with no +# highestConnectedNet call and is independent of PR #3194) that would +# otherwise leave clk unannotated on the fixed binary too. +# +# Hier-crossing nets (carry both flat dbNet + dbModNet on their leaf +# iterms after link_design -hier): +# clk_int : ck_buf/Z -> ff_top/CK, ff_out/CK, sub_inst/ff_sub/CK +# midnet : ff_top/Q -> sub_inst/ff_sub/D +# intermediate : sub_inst/buf_sub/Z -> ff_out/D +# +# Expected outputs: +# +# Fixed binary: +# Found 3 unannotated drivers. +# ff_out/QN +# ff_top/QN +# sub_inst/ff_sub/QN +# Found 0 partially unannotated drivers. +# +# Bug binary: +# Found 6 unannotated drivers. +# ck_buf/Z +# ff_out/QN +# ff_top/Q +# ff_top/QN +# sub_inst/buf_sub/Z +# sub_inst/ff_sub/QN +# Found 0 partially unannotated drivers. +# +# The 3 baseline entries (ff_top/QN, ff_out/QN, sub_inst/ff_sub/QN) +# are dangling DFF QN outputs -- no loads, no wire, no parasitic +# possible. The 3 bug-extras (ck_buf/Z, ff_top/Q, sub_inst/buf_sub/Z) +# are the modnet-wrapped drivers on the hier-crossing nets above: +# findParasiticNet returns their dbModNet wrapper, SPEF-inserted +# entries are keyed by flat dbNet -> map miss on bug binary, hit on +# fixed binary. +source "helpers.tcl" +read_liberty Nangate45/Nangate45_typ.lib +read_lef Nangate45/Nangate45.lef +read_verilog hier_highest_connected_net.v +link_design top -hier + +create_clock -name clk -period 5 [get_ports clk] +set_input_delay -clock clk 0 [get_ports in] +set_output_delay -clock clk 0 [get_ports out] + +read_spef hier_highest_connected_net.spef + +report_parasitic_annotation -report_unannotated diff --git a/src/dbSta/test/hier_highest_connected_net.v b/src/dbSta/test/hier_highest_connected_net.v new file mode 100644 index 00000000000..53d71c9bef3 --- /dev/null +++ b/src/dbSta/test/hier_highest_connected_net.v @@ -0,0 +1,32 @@ +// 2-level hier reg-to-reg design. midnet/intermediate span sub_inst +// boundary so leaf iterms on those nets get both flat dbNet and +// dbModNet wrappers after link_design -hier. Clock buffer ck_buf +// isolates the top clk BTerm so driver iteration doesn't hit the +// top-BTerm bypass branch in Parasitics::findParasiticNet (which is +// independent of PR #3194 and would otherwise leave clk unannotated). + +module sub (din, sub_clk, dout); + input din; + input sub_clk; + output dout; + + wire q_int; + + DFF_X1 ff_sub (.D(din), .CK(sub_clk), .Q(q_int)); + BUF_X1 buf_sub (.A(q_int), .Z(dout)); +endmodule + +module top (in, clk, out); + input in; + input clk; + output out; + + wire clk_int; + wire midnet; + wire intermediate; + + BUF_X1 ck_buf (.A(clk), .Z(clk_int)); + DFF_X1 ff_top (.D(in), .CK(clk_int), .Q(midnet)); + sub sub_inst (.din(midnet), .sub_clk(clk_int), .dout(intermediate)); + DFF_X1 ff_out (.D(intermediate), .CK(clk_int), .Q(out)); +endmodule