@@ -64,6 +64,7 @@ namespace gpl {
6464using odb::dbInst;
6565using odb::dbITerm;
6666using odb::dbMaster;
67+ using odb::dbModule;
6768using odb::dbMTerm;
6869using odb::dbNet;
6970using utl::GPL ;
@@ -171,6 +172,61 @@ const sta::LibertyCell* MBFF::getLibertyCell(const sta::Cell* cell)
171172 return lib_cell;
172173}
173174
175+ namespace {
176+ // Rebind a tray iterm to the given (flat, mod) pair. The plain
177+ // dbITerm::connect(dbNet*) overload only updates the flat side; if a
178+ // stale modnet was attached from an earlier per-orig-flop iteration
179+ // on a shared role pin, it would form an inconsistent (flat, mod)
180+ // pair. Clear the mod side explicitly when only flat is being
181+ // rebound. dbITerm::connect(dbNet*, dbModNet*) cannot be used with a
182+ // null mod_net because it dereferences the modnet unconditionally.
183+ void reconnectIterm (dbITerm* tray_iterm, dbNet* net, odb::dbModNet* mod_net)
184+ {
185+ if (net && mod_net) {
186+ tray_iterm->connect (net, mod_net);
187+ } else if (net) {
188+ tray_iterm->disconnectDbModNet ();
189+ tray_iterm->connect (net);
190+ } else if (mod_net) {
191+ tray_iterm->connect (mod_net);
192+ }
193+ }
194+
195+ // Check whether lib_port appears in any sequential's FuncExpr accessed
196+ // via `get` (e.g., &Sequential::clear or &Sequential::preset). Scans
197+ // both the regular lib_cell sequentials and the test cell's
198+ // sequentials. The test cell's seq.clear()/preset() FuncExpr port set
199+ // holds test-cell port pointers, so the test cell branch resolves the
200+ // matching port by name first.
201+ bool portInSequentialFunc (const sta::LibertyCell* lib_cell,
202+ const sta::LibertyPort* lib_port,
203+ sta::FuncExpr* (sta::Sequential::*get)() const )
204+ {
205+ for (const sta::Sequential& seq : lib_cell->sequentials ()) {
206+ if (const sta::FuncExpr* fe = (seq.*get)()) {
207+ if (fe->hasPort (lib_port)) {
208+ return true ;
209+ }
210+ }
211+ }
212+ if (const sta::LibertyCell* test_cell = lib_cell->testCell ()) {
213+ const sta::LibertyPort* test_lib_port
214+ = test_cell->findLibertyPort (lib_port->name ());
215+ if (test_lib_port == nullptr ) {
216+ return false ;
217+ }
218+ for (const sta::Sequential& seq : test_cell->sequentials ()) {
219+ if (const sta::FuncExpr* fe = (seq.*get)()) {
220+ if (fe->hasPort (test_lib_port)) {
221+ return true ;
222+ }
223+ }
224+ }
225+ }
226+ return false ;
227+ }
228+ } // namespace
229+
174230float MBFF::GetDist (const Point& a, const Point& b)
175231{
176232 return (abs (a.x - b.x ) + abs (a.y - b.y ));
@@ -222,9 +278,14 @@ bool MBFF::IsDPin(dbITerm* iterm)
222278{
223279 dbInst* inst = iterm->getInst ();
224280 const sta::Cell* cell = network_->dbToSta (inst->getMaster ());
225- const sta::LibertyCell* lib_cell = getLibertyCell (cell);
281+ // Use raw libertyCell (not getLibertyCell, which substitutes the
282+ // test cell). portInSequentialFunc scans both the regular and the
283+ // test cell views with the appropriate LibertyPort lookup.
284+ const sta::LibertyCell* lib_cell = network_->libertyCell (cell);
285+ if (lib_cell == nullptr ) {
286+ return false ;
287+ }
226288
227- // check that the iterm isn't a (re)set pin
228289 const sta::Pin* pin = network_->dbToSta (iterm);
229290 if (pin == nullptr ) {
230291 return false ;
@@ -234,13 +295,9 @@ bool MBFF::IsDPin(dbITerm* iterm)
234295 return false ;
235296 }
236297
237- for (const sta::Sequential& seq : lib_cell->sequentials ()) {
238- if (seq.clear () && seq.clear ()->hasPort (lib_port)) {
239- return false ;
240- }
241- if (seq.preset () && seq.preset ()->hasPort (lib_port)) {
242- return false ;
243- }
298+ if (portInSequentialFunc (lib_cell, lib_port, &sta::Sequential::clear)
299+ || portInSequentialFunc (lib_cell, lib_port, &sta::Sequential::preset)) {
300+ return false ;
244301 }
245302
246303 const bool exclude = (IsClockPin (iterm) || IsSupplyPin (iterm)
@@ -347,42 +404,11 @@ bool MBFF::IsClearPin(dbITerm* iterm)
347404 if (lib_port == nullptr ) {
348405 return false ;
349406 }
350-
351407 const sta::LibertyCell* lib_cell = network_->libertyCell (cell);
352408 if (lib_cell == nullptr ) {
353409 return false ;
354410 }
355-
356- // Check the lib cell if the port is a clear.
357- for (const sta::Sequential& seq : lib_cell->sequentials ()) {
358- if (seq.clear () && seq.clear ()->hasPort (lib_port)) {
359- return true ;
360- }
361- }
362-
363- // If it exists, check the test lib cell if the port is a clear.
364- const sta::LibertyCell* test_cell = lib_cell->testCell ();
365- if (test_cell == nullptr ) {
366- return false ;
367- }
368-
369- // Find the equivalent lib_port on the test cell by name.
370- //
371- // TODO: NA - Make retrieving the port on the lib cell possible without doing
372- // a name match each time
373- const sta::LibertyPort* test_lib_port
374- = test_cell->findLibertyPort (lib_port->name ());
375- if (test_lib_port == nullptr ) {
376- return false ;
377- }
378-
379- for (const sta::Sequential& seq : test_cell->sequentials ()) {
380- if (seq.clear () && seq.clear ()->hasPort (test_lib_port)) {
381- return true ;
382- }
383- }
384-
385- return false ;
411+ return portInSequentialFunc (lib_cell, lib_port, &sta::Sequential::clear);
386412}
387413
388414bool MBFF::HasPreset (dbInst* inst)
@@ -414,37 +440,7 @@ bool MBFF::IsPresetPin(dbITerm* iterm)
414440 if (lib_cell == nullptr ) {
415441 return false ;
416442 }
417-
418- // Check the lib cell if the port is a preset.
419- for (const sta::Sequential& seq : lib_cell->sequentials ()) {
420- if (seq.preset () && seq.preset ()->hasPort (lib_port)) {
421- return true ;
422- }
423- }
424-
425- // If it exists, check the test lib cell if the port is a preset.
426- const sta::LibertyCell* test_cell = lib_cell->testCell ();
427- if (test_cell == nullptr ) {
428- return false ;
429- }
430-
431- // Find the equivalent lib_port on the test cell by name.
432- //
433- // TODO: NA - Make retrieving the port on the lib cell possible without doing
434- // a name match each time
435- const sta::LibertyPort* test_lib_port
436- = test_cell->findLibertyPort (lib_port->name ());
437- if (test_lib_port == nullptr ) {
438- return false ;
439- }
440-
441- for (const sta::Sequential& seq : test_cell->sequentials ()) {
442- if (seq.preset () && seq.preset ()->hasPort (test_lib_port)) {
443- return true ;
444- }
445- }
446-
447- return false ;
443+ return portInSequentialFunc (lib_cell, lib_port, &sta::Sequential::preset);
448444}
449445
450446bool MBFF::IsScanCell (dbInst* inst)
@@ -797,8 +793,14 @@ void MBFF::ModifyPinConnections(const std::vector<Flop>& flops,
797793 + " _" + std::to_string (unused_.back ());
798794 unused_.pop_back ();
799795 const int bit_idx = GetBitIdx (trays[tray_idx].slots .size ());
800- auto new_tray = dbInst::create (
801- block_, best_master_[array_mask][bit_idx], new_name.c_str ());
796+ // SeparateFlops partitions by (parent module, mask), so flops in this
797+ // cluster share one parent module; place the new tray there.
798+ dbModule* parent = insts_[flops[i].idx ]->getModule ();
799+ auto new_tray = dbInst::create (block_,
800+ best_master_[array_mask][bit_idx],
801+ new_name.c_str (),
802+ /* physical_only=*/ false ,
803+ parent);
802804 const Point tray_center = GetTrayCenter (array_mask, bit_idx);
803805 new_tray->setLocation (
804806 multiplier_ * (trays[tray_idx].pt .x - tray_center.x ),
@@ -810,6 +812,7 @@ void MBFF::ModifyPinConnections(const std::vector<Flop>& flops,
810812 }
811813
812814 dbNet* clk_net = nullptr ;
815+ odb::dbModNet* clk_mod_net = nullptr ;
813816 for (int i = 0 ; i < num_flops; i++) {
814817 // single bit flop?
815818 if (new_mapping[i].first == std::numeric_limits<int >::max ()) {
@@ -876,56 +879,55 @@ void MBFF::ModifyPinConnections(const std::vector<Flop>& flops,
876879 const bool is_qn_inv = is_q && IsInvertingQPin (iterm);
877880 const std::string orig_port_name = iterm->getMTerm ()->getName ();
878881
882+ // Capture both flat and hierarchical nets of the original iterm
883+ // before disconnecting, then rebind the matching tray iterm to
884+ // both. Preserves the hierarchical netlist after clustering.
879885 dbNet* net = iterm->getNet ();
880- while (net) {
886+ odb::dbModNet* mod_net = iterm->getModNet ();
887+ auto reconnect = [&](dbITerm* tray_iterm) {
888+ reconnectIterm (tray_iterm, net, mod_net);
889+ };
890+ if (net || mod_net) {
881891 iterm->disconnect ();
882892
883- // standard pins
884893 if (is_d) {
885- tray_inst[tray_idx]->findITerm (d_pin->name ().c_str ())-> connect (net );
894+ reconnect ( tray_inst[tray_idx]->findITerm (d_pin->name ().c_str ()));
886895 }
887896 if (is_q) {
888897 if (is_qn_inv) {
889- tray_inst[tray_idx]
890- ->findITerm (qn_pin->name ().c_str ())
891- ->connect (net);
898+ reconnect (tray_inst[tray_idx]->findITerm (qn_pin->name ().c_str ()));
892899 } else {
893- tray_inst[tray_idx]->findITerm (q_pin->name ().c_str ())-> connect (net );
900+ reconnect ( tray_inst[tray_idx]->findITerm (q_pin->name ().c_str ()));
894901 }
895902 }
896903 if (IsSupplyPin (iterm)) {
897904 if (iterm->getSigType () == odb::dbSigType::GROUND ) {
898905 if (ground) {
899- ground-> connect (net );
906+ reconnect (ground );
900907 }
901908 } else {
902909 if (power) {
903- power-> connect (net );
910+ reconnect (power );
904911 }
905912 }
906913 }
907914 if (IsClockPin (iterm)) {
908- // reconnect pins later
915+ // reconnect clock pins later (shared across the tray)
909916 clk_net = net;
917+ clk_mod_net = mod_net;
910918 }
911-
912- // scan pins
913919 if (IsScanIn (iterm)) {
914- scan_in-> connect (net );
920+ reconnect (scan_in );
915921 }
916922 if (IsScanEnable (iterm)) {
917- scan_enable-> connect (net );
923+ reconnect (scan_enable );
918924 }
919-
920- // preset/clear pins
921925 if (IsPresetPin (iterm)) {
922- preset-> connect (net );
926+ reconnect (preset );
923927 }
924928 if (IsClearPin (iterm)) {
925- clear-> connect (net );
929+ reconnect (clear );
926930 }
927-
928- net = iterm->getNet ();
929931 }
930932
931933 // Store original FF→tray pin mapping as a property on the tray
@@ -958,10 +960,11 @@ void MBFF::ModifyPinConnections(const std::vector<Flop>& flops,
958960 std::vector<bool > isConnected (tray_inst.size ());
959961 for (int i = 0 ; i < num_flops; i++) {
960962 if (new_mapping[i].first != std::numeric_limits<int >::max ()) {
961- if (!isConnected[new_mapping[i].first ] && clk_net != nullptr ) {
963+ if (!isConnected[new_mapping[i].first ]
964+ && (clk_net != nullptr || clk_mod_net != nullptr )) {
962965 for (dbITerm* iterm : tray_inst[new_mapping[i].first ]->getITerms ()) {
963966 if (IsClockPin (iterm)) {
964- iterm-> connect ( clk_net);
967+ reconnectIterm (iterm, clk_net, clk_mod_net );
965968 }
966969 }
967970 isConnected[new_mapping[i].first ] = true ;
@@ -2433,23 +2436,44 @@ void MBFF::SeparateFlops(std::vector<std::vector<Flop>>& ffs)
24332436 }
24342437 }
24352438
2439+ // Order modules by id, not pointer value — pointer order varies
2440+ // across runs and would make tray naming / ILP seed consumption
2441+ // non-deterministic.
2442+ struct ModMaskLess
2443+ {
2444+ bool operator ()(const std::pair<dbModule*, Mask>& a,
2445+ const std::pair<dbModule*, Mask>& b) const
2446+ {
2447+ if (a.first != b.first ) {
2448+ return odb::compare_by_id (a.first , b.first );
2449+ }
2450+ return a.second < b.second ;
2451+ }
2452+ };
2453+
24362454 for (const auto & [clk_net, indices] : clk_terms) {
2437- ArrayMaskVector<Flop> flops_by_mask;
2455+ // Partition by (parent module, mask) so flops in different
2456+ // hierarchical modules are never clustered into the same tray.
2457+ std::map<std::pair<dbModule*, Mask>, std::vector<Flop>, ModMaskLess>
2458+ flops_by_mod_mask;
24382459 for (const int idx : indices) {
24392460 const Mask vec_mask = GetArrayMask (insts_[idx], false );
2440- flops_by_mask[vec_mask].push_back (flops_[idx]);
2461+ flops_by_mod_mask[{insts_[idx]->getModule (), vec_mask}].push_back (
2462+ flops_[idx]);
24412463 }
24422464
2443- for (const auto & [mask , flops] : flops_by_mask ) {
2465+ for (const auto & [key , flops] : flops_by_mod_mask ) {
24442466 if (!flops.empty ()) {
24452467 ffs.push_back (flops);
24462468 debugPrint (log_,
24472469 GPL ,
24482470 " mbff" ,
24492471 1 ,
2450- " Flop cluster for net {} with mask {} of size {}" ,
2472+ " Flop cluster for net {} in module {} with mask {} of "
2473+ " size {}" ,
24512474 clk_net->getName (),
2452- mask.to_string (),
2475+ key.first ? key.first ->getHierarchicalName () : " <top>" ,
2476+ key.second .to_string (),
24532477 flops.size ());
24542478 }
24552479 }
0 commit comments