@@ -102,6 +102,151 @@ TEST_F(TestDbSta, TestHierarchyConnectivity)
102102 ASSERT_EQ (bterm_clk->getITerm (), nullptr );
103103}
104104
105+ // Coverage for dbNetwork::findFlatDbNet overloads. Verifies that a flat
106+ // dbNet can be reached from any of: a leaf iterm pin, a top-level bterm
107+ // pin, a hierarchical moditerm pin, a flat dbNet, and a hierarchical
108+ // dbModNet. Pair this with the FlatNet test, which characterizes the
109+ // non-hierarchy-resolving flatNet overloads.
110+ TEST_F (TestDbSta, FindFlatDbNet)
111+ {
112+ std::string test_name = " TestDbSta_0" ;
113+ readVerilogAndSetup (test_name + " .v" );
114+
115+ odb::dbNet* dbnet_net2 = block_->findNet (" net2" );
116+ ASSERT_NE (dbnet_net2, nullptr );
117+
118+ odb::dbModNet* modnet_mod_in = block_->findModNet (" sub_inst/mod_in" );
119+ ASSERT_NE (modnet_mod_in, nullptr );
120+ // The hier net for sub_inst/mod_in shares the flat net "net2".
121+ ASSERT_EQ (modnet_mod_in->findRelatedNet (), dbnet_net2);
122+
123+ // 1. findFlatDbNet(Pin*) on an iterm pin (buf/Z drives net2).
124+ odb::dbInst* buf_inst = block_->findInst (" buf" );
125+ ASSERT_NE (buf_inst, nullptr );
126+ Pin* iterm_pin = db_network_->dbToSta (buf_inst->findITerm (" Z" ));
127+ ASSERT_NE (iterm_pin, nullptr );
128+ EXPECT_EQ (db_network_->findFlatDbNet (iterm_pin), dbnet_net2);
129+
130+ // 2. findFlatDbNet(Pin*) on a top-level bterm pin (in1).
131+ odb::dbBTerm* bterm_in1 = block_->findBTerm (" in1" );
132+ ASSERT_NE (bterm_in1, nullptr );
133+ Pin* bterm_pin = db_network_->dbToSta (bterm_in1);
134+ ASSERT_NE (bterm_pin, nullptr );
135+ EXPECT_EQ (db_network_->findFlatDbNet (bterm_pin), bterm_in1->getNet ());
136+
137+ // 3. findFlatDbNet(Pin*) on a moditerm pin (sub_inst/mod_in).
138+ Pin* moditerm_pin = db_network_->findPin (" sub_inst/mod_in" );
139+ ASSERT_NE (moditerm_pin, nullptr );
140+ EXPECT_EQ (db_network_->findFlatDbNet (moditerm_pin), dbnet_net2);
141+
142+ // 4. findFlatDbNet(Net*) on a flat dbNet returns the same dbNet.
143+ Net* sta_dbnet = db_network_->dbToSta (dbnet_net2);
144+ EXPECT_EQ (db_network_->findFlatDbNet (sta_dbnet), dbnet_net2);
145+
146+ // 5. findFlatDbNet(Net*) on a dbModNet resolves to the related flat
147+ // dbNet.
148+ Net* sta_modnet = db_network_->dbToSta (modnet_mod_in);
149+ EXPECT_EQ (db_network_->findFlatDbNet (sta_modnet), dbnet_net2);
150+
151+ // 6. findFlatDbNet(Net*) on null returns null.
152+ EXPECT_EQ (db_network_->findFlatDbNet (static_cast <Net*>(nullptr )), nullptr );
153+ }
154+
155+ // Characterization tests for dbNetwork::flatNet in the presence of
156+ // hierarchy. flatNet does NOT walk the hierarchy: a hierarchical input
157+ // (moditerm Pin*, dbModNet Net*) yields nullptr even when a related flat
158+ // dbNet exists. Callers that need hierarchical resolution must use
159+ // findFlatDbNet. These expectations pin down the current behavior so any
160+ // future change to flatNet is intentional and reviewed.
161+ TEST_F (TestDbSta, FlatNet)
162+ {
163+ std::string test_name = " TestDbSta_0" ;
164+ readVerilogAndSetup (test_name + " .v" );
165+
166+ odb::dbNet* dbnet_net2 = block_->findNet (" net2" );
167+ ASSERT_NE (dbnet_net2, nullptr );
168+
169+ odb::dbModNet* modnet_mod_in = block_->findModNet (" sub_inst/mod_in" );
170+ ASSERT_NE (modnet_mod_in, nullptr );
171+ // Sanity: the hierarchical net for sub_inst/mod_in shares flat net "net2".
172+ ASSERT_EQ (modnet_mod_in->findRelatedNet (), dbnet_net2);
173+
174+ // 1. flatNet(Pin*) on a leaf iterm pin returns the directly attached
175+ // flat dbNet.
176+ odb::dbInst* buf_inst = block_->findInst (" buf" );
177+ ASSERT_NE (buf_inst, nullptr );
178+ Pin* iterm_pin = db_network_->dbToSta (buf_inst->findITerm (" Z" ));
179+ ASSERT_NE (iterm_pin, nullptr );
180+ EXPECT_EQ (db_network_->flatNet (iterm_pin), dbnet_net2);
181+
182+ // 2. flatNet(Pin*) on a top-level bterm pin returns the directly
183+ // attached flat dbNet.
184+ odb::dbBTerm* bterm_in1 = block_->findBTerm (" in1" );
185+ ASSERT_NE (bterm_in1, nullptr );
186+ Pin* bterm_pin = db_network_->dbToSta (bterm_in1);
187+ ASSERT_NE (bterm_pin, nullptr );
188+ EXPECT_EQ (db_network_->flatNet (bterm_pin), bterm_in1->getNet ());
189+
190+ // 3. flatNet(Pin*) on a hierarchical moditerm pin returns nullptr —
191+ // moditerms attach only to modnets, and flatNet does not resolve through
192+ // the modnet. Callers needing the related flat dbNet must use
193+ // findFlatDbNet instead.
194+ Pin* moditerm_pin = db_network_->findPin (" sub_inst/mod_in" );
195+ ASSERT_NE (moditerm_pin, nullptr );
196+ EXPECT_EQ (db_network_->flatNet (moditerm_pin), nullptr );
197+
198+ // 4. flatNet(Net*) on a flat dbNet returns the same dbNet.
199+ Net* sta_dbnet = db_network_->dbToSta (dbnet_net2);
200+ EXPECT_EQ (db_network_->flatNet (sta_dbnet), dbnet_net2);
201+
202+ // 5. flatNet(Net*) on a hierarchical dbModNet returns nullptr — flatNet
203+ // does not resolve through the modnet to its related flat dbNet.
204+ Net* sta_modnet = db_network_->dbToSta (modnet_mod_in);
205+ EXPECT_EQ (db_network_->flatNet (sta_modnet), nullptr );
206+
207+ // 6. flatNet(Net*) on null returns null.
208+ EXPECT_EQ (db_network_->flatNet (static_cast <Net*>(nullptr )), nullptr );
209+
210+ // 7. flatNet(Term*) on a top-level bterm returns its dbNet.
211+ Term* bterm_term = db_network_->dbToStaTerm (bterm_in1);
212+ ASSERT_NE (bterm_term, nullptr );
213+ EXPECT_EQ (db_network_->flatNet (bterm_term), bterm_in1->getNet ());
214+ }
215+
216+ // Reassociating a moditerm pin must preserve its modnet connection and not
217+ // trip ORD-2026 in connectPin. moditerms only attach to modnets, so the
218+ // flat-net leg of reassociatePinConnection must resolve to nullptr for
219+ // these pins. Regression for the hierarchical repair_tie_fanout flow.
220+ TEST_F (TestDbSta, ReassociateModITermPin)
221+ {
222+ std::string test_name = " TestDbSta_0" ;
223+ readVerilogAndSetup (test_name + " .v" );
224+
225+ Pin* moditerm_pin = db_network_->findPin (" sub_inst/mod_in" );
226+ ASSERT_NE (moditerm_pin, nullptr );
227+
228+ odb::dbITerm* iterm = nullptr ;
229+ odb::dbBTerm* bterm = nullptr ;
230+ odb::dbModITerm* moditerm = nullptr ;
231+ db_network_->staToDb (moditerm_pin, iterm, bterm, moditerm);
232+ ASSERT_EQ (iterm, nullptr );
233+ ASSERT_EQ (bterm, nullptr );
234+ ASSERT_NE (moditerm, nullptr );
235+
236+ odb::dbModNet* original_modnet = moditerm->getModNet ();
237+ ASSERT_NE (original_modnet, nullptr );
238+ // The pin's modnet has a related flat dbNet, so a buggy reassociate that
239+ // forwarded findFlatDbNet's result into connectPin would trigger ORD-2026.
240+ ASSERT_NE (db_network_->findFlatDbNet (moditerm_pin), nullptr );
241+
242+ db_network_->reassociatePinConnection (moditerm_pin);
243+
244+ EXPECT_EQ (moditerm->getModNet (), original_modnet);
245+ EXPECT_TRUE (db_network_->isConnected (db_network_->dbToSta (original_modnet),
246+ moditerm_pin));
247+ db_network_->checkAxioms ();
248+ }
249+
105250// Regression for #10210 (stale Path* dereference in rsz).
106251//
107252// Topology (TestDbSta_StalePrevPath.v):
0 commit comments