|
8 | 8 | #include "db_sta/dbNetwork.hh" |
9 | 9 | #include "gtest/gtest.h" |
10 | 10 | #include "odb/db.h" |
| 11 | +#include "odb/dbTypes.h" |
| 12 | +#include "sta/Graph.hh" |
11 | 13 | #include "sta/NetworkClass.hh" |
| 14 | +#include "sta/Path.hh" |
| 15 | +#include "sta/SdcClass.hh" |
| 16 | +#include "sta/Sta.hh" |
12 | 17 | #include "tst/IntegratedFixture.h" |
13 | 18 |
|
14 | 19 | namespace sta { |
@@ -97,4 +102,71 @@ TEST_F(TestDbSta, TestHierarchyConnectivity) |
97 | 102 | ASSERT_EQ(bterm_clk->getITerm(), nullptr); |
98 | 103 | } |
99 | 104 |
|
| 105 | +// Regression for #10210 (stale Path* dereference in rsz). |
| 106 | +// |
| 107 | +// Topology (TestDbSta_StalePrevPath.v): |
| 108 | +// clk -> b1(BUF) -> inv1(INV) -> nd1(NAND2) -> out1 |
| 109 | +// nd1/A2 <- in2 |
| 110 | +// |
| 111 | +// Flow: |
| 112 | +// 1. Capture drvr_path at nd1/ZN and snapshot prevPath() pointer + pin name |
| 113 | +// 2. Delete upstream b1 + updateTiming -> free |
| 114 | +// 3. Add a fresh BUF + clock + updateTiming -> recycle |
| 115 | +// 4. Assert the captured Path's prev slot has been recycled: pin() |
| 116 | +// decodes to data that belongs to a different instance than nd1's |
| 117 | +// real input. |
| 118 | +TEST_F(TestDbSta, StalePrevPath) |
| 119 | +{ |
| 120 | + const auto* test_info = testing::UnitTest::GetInstance()->current_test_info(); |
| 121 | + const std::string test_name |
| 122 | + = std::string(test_info->test_suite_name()) + "_" + test_info->name(); |
| 123 | + readVerilogAndSetup(test_name + ".v"); |
| 124 | + sta_->updateTiming(true); |
| 125 | + |
| 126 | + Network* network = sta_->network(); |
| 127 | + |
| 128 | + Instance* nd1 = db_network_->dbToSta(block_->findInst("nd1")); |
| 129 | + Path* drvr_path = sta_->vertexWorstArrivalPath( |
| 130 | + sta_->ensureGraph()->pinDrvrVertex(network->findPin(nd1, "ZN")), |
| 131 | + MinMax::max()); |
| 132 | + ASSERT_NE(drvr_path, nullptr); |
| 133 | + ASSERT_EQ(network->pathName(drvr_path->pin(sta_.get())), "nd1/ZN"); |
| 134 | + const Path* pre_addr = drvr_path->prevPath(); |
| 135 | + ASSERT_NE(pre_addr, nullptr); |
| 136 | + const std::string pre_pin_name = network->pathName(pre_addr->pin(sta_.get())); |
| 137 | + |
| 138 | + // 2. Free upstream Path[] slots. |
| 139 | + sta_->deleteInstance(db_network_->dbToSta(block_->findInst("b1"))); |
| 140 | + sta_->updateTiming(true); |
| 141 | + |
| 142 | + // 3. Recycle freed slots via a single fresh BUF driven by a new clock. |
| 143 | + odb::dbNet* in3_net = odb::dbNet::create(block_, "in3"); |
| 144 | + odb::dbBTerm* new_bt = odb::dbBTerm::create(in3_net, "in3"); |
| 145 | + new_bt->setIoType(odb::dbIoType::INPUT); |
| 146 | + odb::dbNet* nfan_net = odb::dbNet::create(block_, "nfan"); |
| 147 | + odb::dbInst* bnew |
| 148 | + = odb::dbInst::create(block_, db_->findMaster("BUF_X1"), "bnew"); |
| 149 | + bnew->findITerm("A")->connect(in3_net); |
| 150 | + bnew->findITerm("Z")->connect(nfan_net); |
| 151 | + |
| 152 | + PinSet clk2_pins(db_network_); |
| 153 | + clk2_pins.insert(db_network_->dbToSta(new_bt)); |
| 154 | + FloatSeq clk2_waveform = {0.0f, 0.1f}; |
| 155 | + sta_->makeClock( |
| 156 | + "clk2", clk2_pins, false, 0.2f, clk2_waveform, "", sta_->cmdMode()); |
| 157 | + sta_->updateTiming(true); |
| 158 | + |
| 159 | + // 4. Staleness evidence. Pointer address is same but pin name has changed. |
| 160 | + const Path* post_addr = drvr_path->prevPath(); |
| 161 | + const std::string post_pin_name |
| 162 | + = post_addr ? network->pathName(post_addr->pin(sta_.get())) |
| 163 | + : std::string("<null>"); |
| 164 | + |
| 165 | + EXPECT_EQ(pre_addr, post_addr) |
| 166 | + << "stale-pointer signature: prev_path_ address unchanged"; |
| 167 | + EXPECT_NE(pre_pin_name, post_pin_name) |
| 168 | + << "but slot content should differ after free+reuse. before=" |
| 169 | + << pre_pin_name << " after=" << post_pin_name; |
| 170 | +} |
| 171 | + |
100 | 172 | } // namespace sta |
0 commit comments