2323#include " infrastructure/Grid.h"
2424#include " infrastructure/Objects.h"
2525#include " infrastructure/Padding.h"
26+ #include " infrastructure/architecture.h"
2627#include " infrastructure/network.h"
2728#include " odb/db.h"
2829#include " odb/geom.h"
@@ -533,18 +534,39 @@ bool NegotiationLegalizer::initFromDb()
533534 grid_w_ = dpl_grid->getRowSiteCount ().v ;
534535 grid_h_ = dpl_grid->getRowCount ().v ;
535536
536- // Assign power-rail types using row-index parity (VSS at even rows).
537- // Replace with explicit LEF pg_pin parsing for advanced PDKs.
537+ // Assign power-rail types from DB row orientations.
538+ // R0/MY = right-side-up → VSS rail at bottom; MX/R180 = flipped → VDD at
539+ // bottom. Rows missing from the DB default to VSS.
538540 row_rail_.clear ();
539- row_rail_.resize (grid_h_);
540- for (int r = 0 ; r < grid_h_; ++r) {
541- row_rail_[r] = (r % 2 == 0 ) ? NLPowerRailType::kVss : NLPowerRailType::kVdd ;
541+ row_rail_.resize (grid_h_, NLPowerRailType::kVss );
542+ for (auto * db_row : block->getRows ()) {
543+ const int y_dbu = db_row->getOrigin ().y () - die_ylo_;
544+ if (y_dbu < 0 || y_dbu % row_height_ != 0 ) {
545+ continue ;
546+ }
547+ const int r = y_dbu / row_height_;
548+ if (r >= grid_h_) {
549+ continue ;
550+ }
551+ const auto orient = db_row->getOrient ();
552+ row_rail_[r]
553+ = (orient == odb::dbOrientType::MX || orient == odb::dbOrientType::R180 )
554+ ? NLPowerRailType::kVdd
555+ : NLPowerRailType::kVss ;
542556 }
543557
544558 // Build NegCell records from all placed instances.
545559 cells_.clear ();
546560 cells_.reserve (block->getInsts ().size ());
547561
562+ // Cache region boundaries converted to grid coordinates, keyed by dbRegion*.
563+ struct RegionRectInline
564+ {
565+ int xlo, ylo, xhi, yhi;
566+ };
567+ std::unordered_map<odb::dbRegion*, std::vector<RegionRectInline>>
568+ region_rect_cache;
569+
548570 for (auto * db_inst : block->getInsts ()) {
549571 const auto status = db_inst->getPlacementStatus ();
550572 if (status == odb::dbPlacementStatus::NONE ) {
@@ -624,7 +646,54 @@ bool NegotiationLegalizer::initFromDb()
624646 // in original DPL.
625647 // they achieve the same objective, and the previous is more simple,
626648 // consider replacing this.
627- if (!isValidSite (cell.init_x , cell.init_y )) {
649+ // For region-constrained cells, collect the region rects in grid
650+ // coordinates so the BFS below can verify containment.
651+ // initFenceRegions() has not run yet, so we read ODB directly.
652+ // Instances reach their region via a GROUP, not via dbInst::region_,
653+ // so we must check both paths.
654+ odb::dbRegion* odb_region = db_inst->getRegion ();
655+ if (odb_region == nullptr ) {
656+ auto * grp = db_inst->getGroup ();
657+ if (grp != nullptr ) {
658+ odb_region = grp->getRegion ();
659+ }
660+ }
661+ // Look up (or populate) the cache for this region.
662+ const std::vector<RegionRectInline>* region_rects_ptr = nullptr ;
663+ if (odb_region != nullptr ) {
664+ auto it = region_rect_cache.find (odb_region);
665+ if (it == region_rect_cache.end ()) {
666+ std::vector<RegionRectInline> rects;
667+ for (auto * box : odb_region->getBoundaries ()) {
668+ RegionRectInline r;
669+ r.xlo = (box->xMin () - die_xlo_) / site_width_;
670+ r.ylo = (box->yMin () - die_ylo_) / row_height_;
671+ r.xhi = (box->xMax () - die_xlo_) / site_width_;
672+ r.yhi = (box->yMax () - die_ylo_) / row_height_;
673+ rects.push_back (r);
674+ }
675+ it = region_rect_cache.emplace (odb_region, std::move (rects)).first ;
676+ }
677+ region_rects_ptr = &it->second ;
678+ }
679+ // Returns true when (gx,gy) satisfies the region constraint:
680+ // region-constrained cells must land inside their region;
681+ // unconstrained cells have no restriction here (negotiation handles it).
682+ auto isInRegionOk = [&](int gx, int gy) -> bool {
683+ if (region_rects_ptr == nullptr ) {
684+ return true ;
685+ }
686+ for (const auto & r : *region_rects_ptr) {
687+ if (gx >= r.xlo && gy >= r.ylo && gx + cell.width <= r.xhi
688+ && gy + cell.height <= r.yhi ) {
689+ return true ;
690+ }
691+ }
692+ return false ;
693+ };
694+
695+ if (!isValidSite (cell.init_x , cell.init_y )
696+ || !isInRegionOk (cell.init_x , cell.init_y )) {
628697 debugPrint (logger_,
629698 utl::DPL ,
630699 " negotiation" ,
@@ -666,7 +735,7 @@ bool NegotiationLegalizer::initFromDb()
666735 while (!pq.empty ()) {
667736 auto [dist, gx, gy] = pq.top ();
668737 pq.pop ();
669- if (isValidSite (gx, gy)) {
738+ if (isValidSite (gx, gy) && isInRegionOk (gx, gy) ) {
670739 cell.init_x = gx;
671740 cell.init_y = gy;
672741 cell.x = cell.init_x ;
@@ -680,25 +749,59 @@ bool NegotiationLegalizer::initFromDb()
680749 }
681750 }
682751
683- cell.rail_type = inferRailType (cell.init_y );
684- // If the instance is currently flipped relative to the row's standard
685- // orientation, its internal rail design is opposite of the row's bottom
686- // rail.
687- auto siteOrient = dpl_grid->getSiteOrientation (
688- GridX{cell.init_x }, GridY{cell.init_y }, master->getSite ());
689- if (siteOrient.has_value () && db_inst->getOrient () != siteOrient.value ()) {
690- cell.rail_type = (cell.rail_type == NLPowerRailType::kVss )
691- ? NLPowerRailType::kVdd
692- : NLPowerRailType::kVss ;
752+ // Derive power-rail types directly from the LEF geometry stored by the
753+ // Network — never infer from the cell's current row or orientation.
754+ //
755+ // rail_type = bottom rail in R0 (unflipped) orientation.
756+ // For most CORE cells this is kVss (VSS at bottom).
757+ // rail_type_flipped = bottom rail in MX (flipped) orientation, which
758+ // equals the TOP rail in R0. For most cells: kVdd.
759+ // For symmetric multi-height cells whose VSS appears
760+ // at both top and bottom (e.g. some double-height
761+ // flops): kVss — meaning a flip cannot resolve a
762+ // VDD-bottom row mismatch.
763+ {
764+ int bot_pwr = Architecture::Row::Power_UNK;
765+ int top_pwr = Architecture::Row::Power_UNK;
766+ if (network_ != nullptr ) {
767+ if (Master* dpl_master = network_->getMaster (master)) {
768+ bot_pwr = dpl_master->getBottomPowerType ();
769+ top_pwr = dpl_master->getTopPowerType ();
770+ }
771+ }
772+ auto toRailType = [](int pwr, NLPowerRailType fallback) {
773+ if (pwr == Architecture::Row::Power_VSS) {
774+ return NLPowerRailType::kVss ;
775+ }
776+ if (pwr == Architecture::Row::Power_VDD) {
777+ return NLPowerRailType::kVdd ;
778+ }
779+ return fallback;
780+ };
781+ cell.rail_type = toRailType (bot_pwr, NLPowerRailType::kVss );
782+ cell.rail_type_flipped = toRailType (top_pwr, NLPowerRailType::kVdd );
693783 }
694784
695785 cell.flippable
696786 = master->getSymmetryX (); // X-symmetry allows vertical flip (MX)
697- if (cell.height % 2 == 1 ) {
698- // For 1-row cells, we usually assume they are flippable in most PDKs.
787+ if (cell.height == 1 ) {
788+ // Consider all single height cells flippable
699789 cell.flippable = true ;
700790 }
701791
792+ debugPrint (
793+ logger_,
794+ utl::DPL ,
795+ " negotiation" ,
796+ 1 ,
797+ " DEBUG cell init: {} height={} flippable={} rail_type={} "
798+ " rail_type_flipped={}" ,
799+ db_inst->getName (),
800+ cell.height ,
801+ cell.flippable ,
802+ cell.rail_type == NLPowerRailType::kVss ? " kVss" : " kVdd" ,
803+ cell.rail_type_flipped == NLPowerRailType::kVss ? " kVss" : " kVdd" );
804+
702805 if (padding_ != nullptr ) {
703806 cell.pad_left = padding_->padLeft (db_inst).v ;
704807 cell.pad_right = padding_->padRight (db_inst).v ;
@@ -710,25 +813,22 @@ bool NegotiationLegalizer::initFromDb()
710813 return true ;
711814}
712815
713- NLPowerRailType NegotiationLegalizer::inferRailType (int rowIdx) const
714- {
715- if (rowIdx >= 0 && rowIdx < static_cast <int >(row_rail_.size ())) {
716- return row_rail_[rowIdx];
717- }
718- return NLPowerRailType::kVss ;
719- }
720-
721816void NegotiationLegalizer::buildGrid ()
722817{
723818 Grid* dplGrid = opendp_->grid_ .get ();
724819
725- // Reset all pixels to default negotiation state.
820+ // Reset all pixels to default negotiation state. Also clear any cell
821+ // and padding pointers (including dummy_cell_ set by groupInitPixels2 for
822+ // region boundaries) — fixed cells and movable cells are re-painted later
823+ // by syncAllCellsToDplGrid() before any DRC check runs.
726824 for (int gy = 0 ; gy < grid_h_; ++gy) {
727825 for (int gx = 0 ; gx < grid_w_; ++gx) {
728826 Pixel& pixel = dplGrid->pixel (GridY{gy}, GridX{gx});
729827 pixel.capacity = pixel.is_valid ? 1 : 0 ;
730828 pixel.usage = 0 ;
731829 pixel.hist_cost = 1.0 ;
830+ pixel.cell = nullptr ;
831+ pixel.padding_reserved_by = nullptr ;
732832 }
733833 }
734834
@@ -793,11 +893,20 @@ void NegotiationLegalizer::initFenceRegions()
793893 }
794894
795895 // Map each instance to its fence region (if any).
896+ // Instances are assigned to regions via GROUPS in DEF, which sets
897+ // dbInst::group_ but NOT dbInst::region_. We must go through the group.
796898 for (auto & cell : cells_) {
797899 if (cell.db_inst == nullptr ) {
798900 continue ;
799901 }
800- auto * region = cell.db_inst ->getRegion ();
902+ // Try direct region first, then group-based region.
903+ odb::dbRegion* region = cell.db_inst ->getRegion ();
904+ if (region == nullptr ) {
905+ auto * grp = cell.db_inst ->getGroup ();
906+ if (grp != nullptr ) {
907+ region = grp->getRegion ();
908+ }
909+ }
801910 if (region == nullptr ) {
802911 continue ;
803912 }
@@ -973,14 +1082,29 @@ bool NegotiationLegalizer::isValidRow(int rowIdx,
9731082 return false ;
9741083 }
9751084 }
976- const NLPowerRailType rowBot = row_rail_[rowIdx];
977- if (cell.height % 2 == 1 ) {
978- // Odd-height: bottom rail must match, or cell can be vertically flipped.
979- return cell.flippable || (rowBot == cell.rail_type );
980- }
981- // Even-height: bottom boundary must be the correct rail type, and the
982- // cell may only move by an even number of rows.
983- return rowBot == cell.rail_type ;
1085+ const NLPowerRailType row_bottom_rail = row_rail_[rowIdx];
1086+ // row and cell rail must match, or cell can be flipped.
1087+ auto railStr = [](NLPowerRailType r) {
1088+ return r == NLPowerRailType::kVss ? " kVss" : " kVdd" ;
1089+ };
1090+ bool ret = (row_bottom_rail == cell.rail_type )
1091+ || (cell.flippable && row_bottom_rail == cell.rail_type_flipped );
1092+ debugPrint (
1093+ logger_,
1094+ utl::DPL ,
1095+ " negotiation" ,
1096+ 1 ,
1097+ " rowIdx: {}, row_bottom_rail: {}, cell: {}, cell.rail_type: {}, "
1098+ " rail_type_flipped: {}, flippable: {}, rail match: {}, is_valid: {}" ,
1099+ rowIdx,
1100+ railStr (row_bottom_rail),
1101+ cell.db_inst ? cell.db_inst ->getName () : " ?" ,
1102+ railStr (cell.rail_type ),
1103+ railStr (cell.rail_type_flipped ),
1104+ cell.flippable ,
1105+ (row_bottom_rail == cell.rail_type ),
1106+ ret);
1107+ return ret;
9841108}
9851109
9861110bool NegotiationLegalizer::respectsFence (int cell_idx, int x, int y) const
@@ -998,6 +1122,7 @@ bool NegotiationLegalizer::respectsFence(int cell_idx, int x, int y) const
9981122 return fences_[cell.fence_id ].contains (x, y, cell.width , cell.height );
9991123}
10001124
1125+ // TODO: remove this function!
10011126std::pair<int , int > NegotiationLegalizer::snapToLegal (int cell_idx,
10021127 int x,
10031128 int y) const
@@ -1030,7 +1155,7 @@ std::pair<int, int> NegotiationLegalizer::snapToLegal(int cell_idx,
10301155 }
10311156 }
10321157
1033- if (ok) {
1158+ if (ok && respectsFence (cell_idx, tx, r) ) {
10341159 const int dx = std::abs (tx - x);
10351160 if (dx < local_best_dx) {
10361161 local_best_dx = dx;
0 commit comments