diff --git a/src/mpl/src/snapper.cpp b/src/mpl/src/snapper.cpp index c7e5837e14d..ab4f8fd1456 100644 --- a/src/mpl/src/snapper.cpp +++ b/src/mpl/src/snapper.cpp @@ -40,7 +40,41 @@ void Snapper::snap(const odb::dbTechLayerDir& target_direction) : inst_->getOrigin().y(); if (layers_data_list.empty()) { + const int initial_origin = origin; alignWithManufacturingGrid(origin); + + if (!isOriginInsideCore(origin, target_direction)) { + // Grid rounding can move an initially contained macro past the core. + const odb::Rect& macro_bbox = inst_->getBBox()->getBox(); + const odb::Rect& core = inst_->getBlock()->getCoreArea(); + const int manufacturing_grid + = inst_->getDb()->getTech()->getManufacturingGrid(); + const int lower_bound + = initial_origin + + (target_direction == odb::dbTechLayerDir::VERTICAL + ? core.xMin() - macro_bbox.xMin() + : core.yMin() - macro_bbox.yMin()); + const int upper_bound + = initial_origin + + (target_direction == odb::dbTechLayerDir::VERTICAL + ? core.xMax() - macro_bbox.xMax() + : core.yMax() - macro_bbox.yMax()); + const int minimum_origin + = static_cast(std::ceil( + lower_bound / static_cast(manufacturing_grid))) + * manufacturing_grid; + const int maximum_origin + = static_cast(std::floor( + upper_bound / static_cast(manufacturing_grid))) + * manufacturing_grid; + + if (minimum_origin > maximum_origin) { + setOrigin(initial_origin, target_direction); + return; + } + origin = std::clamp(origin, minimum_origin, maximum_origin); + } + setOrigin(origin, target_direction); return; } @@ -61,14 +95,33 @@ void Snapper::snap(const odb::dbTechLayerDir& target_direction) starting_position_index -= 1; } - snapPinToPosition(lowest_grid_pin, - lowest_grid_positions[starting_position_index], - target_direction); - attemptSnapToExtraPatterns( starting_position_index, layers_data_list, target_direction); } +bool Snapper::isOriginInsideCore( + const int origin, + const odb::dbTechLayerDir& target_direction) const +{ + const odb::Rect& macro_bbox = inst_->getBBox()->getBox(); + const odb::Rect& core = inst_->getBlock()->getCoreArea(); + if (core.dx() <= 0 || core.dy() <= 0) { + return true; + } + + const int current_origin = target_direction == odb::dbTechLayerDir::VERTICAL + ? inst_->getOrigin().x() + : inst_->getOrigin().y(); + const int displacement = origin - current_origin; + + if (target_direction == odb::dbTechLayerDir::VERTICAL) { + return macro_bbox.xMin() + displacement >= core.xMin() + && macro_bbox.xMax() + displacement <= core.xMax(); + } + return macro_bbox.yMin() + displacement >= core.yMin() + && macro_bbox.yMax() + displacement <= core.yMax(); +} + void Snapper::setOrigin(const int origin, const odb::dbTechLayerDir& target_direction) { @@ -178,13 +231,19 @@ int Snapper::getPinOffset(odb::dbITerm* pin, return pin_offset; } +int Snapper::getSnappedOrigin(const int position, const int pin_offset) +{ + int origin = position - pin_offset; + alignWithManufacturingGrid(origin); + return origin; +} + void Snapper::snapPinToPosition(odb::dbITerm* pin, - int position, + const int position, const odb::dbTechLayerDir& direction) { - int origin = position - getPinOffset(pin, direction); - alignWithManufacturingGrid(origin); - setOrigin(origin, direction); + setOrigin(getSnappedOrigin(position, getPinOffset(pin, direction)), + direction); } void Snapper::getTrackGridPattern(odb::dbTrackGrid* track_grid, @@ -216,33 +275,45 @@ void Snapper::attemptSnapToExtraPatterns( odb::dbITerm* snap_pin = layers_data_list[0].pins[0]; const std::vector& positions = layers_data_list[0].available_positions; - - int best_index = start_index; - int best_snapped_pins = 0; - + std::vector legal_positions; + legal_positions.reserve(total_attempts + 1); + const int pin_offset = getPinOffset(snap_pin, target_direction); + // Keep the existing nearest-first order, but cull before moving the macro. for (int i = 0; i <= total_attempts; i++) { int steps = (i % 2 == 1) ? (i + 1) / 2 : -(i / 2); - int current_index = start_index + steps; - if (current_index < 0 || current_index >= layers_data_list[0].available_positions.size()) { continue; } - snapPinToPosition(snap_pin, positions[current_index], target_direction); + const int position = positions[current_index]; + if (isOriginInsideCore(getSnappedOrigin(position, pin_offset), + target_direction)) { + legal_positions.push_back(position); + } + } + if (legal_positions.empty()) { + // Keeping the original location must not bypass mandatory pin-grid rules. + totalAlignedPins(layers_data_list, target_direction, true); + return; + } + int best_position = legal_positions.front(); + int best_snapped_pins = -1; + for (const int position : legal_positions) { + snapPinToPosition(snap_pin, position, target_direction); int snapped_pins = totalAlignedPins(layers_data_list, target_direction); if (snapped_pins > best_snapped_pins) { best_snapped_pins = snapped_pins; - best_index = current_index; + best_position = position; if (best_snapped_pins == total_pins) { break; } } } - snapPinToPosition(snap_pin, positions[best_index], target_direction); + snapPinToPosition(snap_pin, best_position, target_direction); if (best_snapped_pins != total_pins) { totalAlignedPins(layers_data_list, target_direction, true); diff --git a/src/mpl/src/snapper.h b/src/mpl/src/snapper.h index ea01a21a382..5fc76fc97d5 100644 --- a/src/mpl/src/snapper.h +++ b/src/mpl/src/snapper.h @@ -47,6 +47,8 @@ class Snapper void snap(const odb::dbTechLayerDir& target_direction); void alignWithManufacturingGrid(int& origin); + bool isOriginInsideCore(int origin, + const odb::dbTechLayerDir& target_direction) const; void setOrigin(int origin, const odb::dbTechLayerDir& target_direction); int totalAlignedPins(const LayerDataList& layers_data_list, const odb::dbTechLayerDir& direction, @@ -61,6 +63,7 @@ class Snapper int& step, const odb::dbTechLayerDir& target_direction); int getPinOffset(odb::dbITerm* pin, const odb::dbTechLayerDir& direction); + int getSnappedOrigin(int position, int pin_offset); void snapPinToPosition(odb::dbITerm* pin, int position, const odb::dbTechLayerDir& direction); diff --git a/src/mpl/test/boundary_push1.defok b/src/mpl/test/boundary_push1.defok index 3a0c036baa1..4e75831f52f 100644 --- a/src/mpl/test/boundary_push1.defok +++ b/src/mpl/test/boundary_push1.defok @@ -183,10 +183,10 @@ TRACKS Y 140 DO 282 STEP 3200 LAYER metal9 ; TRACKS X 190 DO 282 STEP 3200 LAYER metal10 ; TRACKS Y 140 DO 282 STEP 3200 LAYER metal10 ; COMPONENTS 4 ; - - MACRO_1 HM_100x100_1x1 + FIXED ( 239900 242530 ) S ; + - MACRO_1 HM_100x100_1x1 + FIXED ( 239900 242250 ) S ; - MACRO_2 HM_100x100_1x1 + FIXED ( 239900 330 ) S ; - MACRO_3 HM_100x100_1x1 + FIXED ( 140 330 ) S ; - - MACRO_4 HM_100x100_1x1 + FIXED ( 140 242530 ) S ; + - MACRO_4 HM_100x100_1x1 + FIXED ( 140 242250 ) S ; END COMPONENTS PINS 4 ; - io_1 + NET io_1 + DIRECTION INPUT + USE SIGNAL ; @@ -196,9 +196,9 @@ PINS 4 ; END PINS BLOCKAGES 4 ; - PLACEMENT + SOFT + COMPONENT MACRO_3 RECT ( 0 190 ) ( 200280 200470 ) ; - - PLACEMENT + SOFT + COMPONENT MACRO_4 RECT ( 0 242390 ) ( 200280 442670 ) ; + - PLACEMENT + SOFT + COMPONENT MACRO_4 RECT ( 0 242110 ) ( 200280 442390 ) ; - PLACEMENT + SOFT + COMPONENT MACRO_2 RECT ( 239760 190 ) ( 440040 200470 ) ; - - PLACEMENT + SOFT + COMPONENT MACRO_1 RECT ( 239760 242390 ) ( 440040 442670 ) ; + - PLACEMENT + SOFT + COMPONENT MACRO_1 RECT ( 239760 242110 ) ( 440040 442390 ) ; END BLOCKAGES NETS 4 ; - io_1 ( PIN io_1 ) + USE SIGNAL ; diff --git a/src/mpl/test/cpp/TestSnapper.cpp b/src/mpl/test/cpp/TestSnapper.cpp index 3c1d0b661c2..1284b4f5ed8 100644 --- a/src/mpl/test/cpp/TestSnapper.cpp +++ b/src/mpl/test/cpp/TestSnapper.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2023-2025, The OpenROAD Authors +#include #include #include #include @@ -47,6 +48,176 @@ TEST_F(TestSnapper, ManufacturingGrid) EXPECT_EQ(macro->getOrigin().y(), 1020); } +// Keeps a macro inside a core that has no legal manufacturing-grid coordinate. +TEST_F(TestSnapper, ManufacturingGridPreservesCoreContainedOrigin) +{ + db_->getTech()->setManufacturingGrid(10); + odb::dbBlock* block = db_->getChip()->getBlock(); + block->setCoreArea(odb::Rect(5, 5, 105, 105)); + + odb::dbMaster* macro_master + = odb::dbMaster::create(db_->findLib("lib"), "macro_master"); + macro_master->setWidth(100); + macro_master->setHeight(100); + macro_master->setType(odb::dbMasterType::BLOCK); + macro_master->setFrozen(); + + odb::dbInst* macro = odb::dbInst::create(block, macro_master, "macro"); + macro->setOrigin(5, 5); + snapper_->setMacro(macro); + snapper_->snapMacro(); + + EXPECT_EQ(macro->getOrigin().x(), 5); + EXPECT_EQ(macro->getOrigin().y(), 5); +} + +class TestSnapperCoreBoundary : public TestSnapper, + public testing::WithParamInterface +{ + protected: + odb::dbInst* makeMacro(bool pins, int pin_min = 4, int height = 100) + { + db_->getTech()->setManufacturingGrid(10); + auto* block = db_->getChip()->getBlock(); + auto* master = odb::dbMaster::create(db_->findLib("lib"), "macro_master"); + master->setWidth(100); + master->setHeight(height); + master->setType(odb::dbMasterType::BLOCK); + if (pins) { + for (const auto direction : + {odb::dbTechLayerDir::VERTICAL, odb::dbTechLayerDir::HORIZONTAL}) { + const char* name + = direction == odb::dbTechLayerDir::VERTICAL ? "V" : "H"; + auto* layer = odb::dbTechLayer::create( + db_->getTech(), name, odb::dbTechLayerType::DEFAULT); + layer->setDirection(direction); + auto* grid = odb::dbTrackGrid::create(block, layer); + if (direction == odb::dbTechLayerDir::VERTICAL) { + grid->addGridPatternX(0, 22, 10); + } else { + grid->addGridPatternY(0, 22, 10); + } + auto* term = odb::dbMTerm::create( + master, name, odb::dbIoType::INPUT, odb::dbSigType::SIGNAL); + auto* pin = odb::dbMPin::create(term); + odb::dbBox::create( + pin, layer, pin_min, pin_min, pin_min + 2, pin_min + 2); + } + } + master->setFrozen(); + auto* macro = odb::dbInst::create(block, master, "macro"); + const std::array orientations{ + odb::dbOrientType::R0, + odb::dbOrientType::R90, + odb::dbOrientType::R180, + odb::dbOrientType::R270, + odb::dbOrientType::MX, + odb::dbOrientType::MY, + odb::dbOrientType::MXR90, + odb::dbOrientType::MYR90}; + macro->setOrient(orientations[GetParam()]); + snapper_->setMacro(macro); + return macro; + } +}; + +TEST_P(TestSnapperCoreBoundary, ManufacturingRoundingAtCoreEdges) +{ + auto* macro = makeMacro(false); + auto* block = db_->getChip()->getBlock(); + const odb::Rect core(5, 5, 205, 205); + block->setCoreArea(core); + for (const int x : {5, 105}) { + for (const int y : {5, 105}) { + SCOPED_TRACE(testing::Message() << "location=" << x << "," << y); + macro->setLocation(x, y); + ASSERT_TRUE(core.contains(macro->getBBox()->getBox())); + snapper_->snapMacro(); + const auto& box = macro->getBBox()->getBox(); + EXPECT_TRUE(core.contains(box)); + EXPECT_EQ(box.xMin(), x == 5 ? 10 : 100); + EXPECT_EQ(box.yMin(), y == 5 ? 10 : 100); + } + } +} + +TEST_P(TestSnapperCoreBoundary, NoManufacturingGridCoordinatePreservesOrigin) +{ + auto* macro = makeMacro(false); + const odb::Rect core(5, 5, 105, 105); + macro->getBlock()->setCoreArea(core); + macro->setLocation(5, 5); + const auto origin = macro->getOrigin(); + snapper_->snapMacro(); + EXPECT_EQ(macro->getOrigin(), origin); + EXPECT_EQ(macro->getBBox()->getBox(), core); +} + +TEST_P(TestSnapperCoreBoundary, TrackCandidateIncludesManufacturingRounding) +{ + auto* macro = makeMacro(true); + const odb::Rect core(5, 5, 205, 205); + macro->getBlock()->setCoreArea(core); + macro->setLocation(105, 105); + ASSERT_TRUE(core.contains(macro->getBBox()->getBox())); + snapper_->snapMacro(); + const auto& box = macro->getBBox()->getBox(); + EXPECT_TRUE(core.contains(box)); + EXPECT_EQ(box.xMin(), 100); + EXPECT_EQ(box.yMin(), 100); +} + +TEST_P(TestSnapperCoreBoundary, NoLegalTrackCandidatePreservesOrigin) +{ + auto* macro = makeMacro(true); + const odb::Rect core(5, 5, 105, 105); + macro->getBlock()->setCoreArea(core); + macro->setLocation(5, 5); + const auto origin = macro->getOrigin(); + snapper_->snapMacro(); + EXPECT_EQ(macro->getOrigin(), origin); + EXPECT_EQ(macro->getBBox()->getBox(), core); +} + +TEST_P(TestSnapperCoreBoundary, NonsquareRotatedMacroStaysInsideCore) +{ + auto* macro = makeMacro(true, 4, 60); + const odb::Rect core(5, 5, 205, 205); + macro->getBlock()->setCoreArea(core); + const int x = core.xMax() - macro->getBBox()->getBox().dx(); + const int y = core.yMax() - macro->getBBox()->getBox().dy(); + macro->setLocation(x, y); + ASSERT_TRUE(core.contains(macro->getBBox()->getBox())); + snapper_->snapMacro(); + const auto& box = macro->getBBox()->getBox(); + EXPECT_TRUE(core.contains(box)); + EXPECT_EQ(box.xMin(), x - 5); + EXPECT_EQ(box.yMin(), y - 5); +} + +TEST_P(TestSnapperCoreBoundary, NoLegalCandidateStillChecksRequiredGrid) +{ + auto* macro = makeMacro(true, 0); + const odb::Rect core(5, 5, 105, 105); + macro->getBlock()->setCoreArea(core); + macro->setLocation(5, 5); + db_->getTech()->findLayer("H")->setRightWayOnGridOnly(true); + db_->getTech()->findLayer("V")->setRightWayOnGridOnly(true); + const auto origin = macro->getOrigin(); + try { + snapper_->snapMacro(); + FAIL() << "Expected an unsnappable RightWayOnGridOnly pin to fail"; + } catch (const std::exception& error) { + EXPECT_STREQ(error.what(), "MPL-0005"); + } + EXPECT_EQ(macro->getOrigin(), origin); + EXPECT_EQ(macro->getBBox()->getBox(), core); +} + +INSTANTIATE_TEST_SUITE_P(AllOrientations, + TestSnapperCoreBoundary, + testing::Range(0, 8)); + // Snaps a macro using one horizontal layer, aligning one pin TEST_F(TestSnapper, SingleLayer) { diff --git a/src/mpl/test/fixed_macros2.defok b/src/mpl/test/fixed_macros2.defok index adfbbc09995..bf447d67ee5 100644 --- a/src/mpl/test/fixed_macros2.defok +++ b/src/mpl/test/fixed_macros2.defok @@ -173,7 +173,7 @@ TRACKS X 190 DO 282 STEP 3200 LAYER metal10 ; TRACKS Y 140 DO 282 STEP 3200 LAYER metal10 ; COMPONENTS 402 ; - MACRO_1 HM_100x100_1x1 + FIXED ( 0 0 ) N ; - - MACRO_2 HM_100x100_1x1 + FIXED ( 140 242530 ) S ; + - MACRO_2 HM_100x100_1x1 + FIXED ( 140 242250 ) S ; - _001_ DFF_X1 + PLACED ( 316860 135060 ) N ; - _002_ DFF_X1 + PLACED ( 316860 135060 ) N ; - _003_ DFF_X1 + PLACED ( 316860 135060 ) N ; @@ -577,7 +577,7 @@ COMPONENTS 402 ; END COMPONENTS BLOCKAGES 2 ; - PLACEMENT + SOFT + COMPONENT MACRO_1 RECT ( -140 -140 ) ( 200140 200140 ) ; - - PLACEMENT + SOFT + COMPONENT MACRO_2 RECT ( 0 242390 ) ( 200280 442670 ) ; + - PLACEMENT + SOFT + COMPONENT MACRO_2 RECT ( 0 242110 ) ( 200280 442390 ) ; END BLOCKAGES NETS 0 ; END NETS diff --git a/src/mpl/test/halos1.defok b/src/mpl/test/halos1.defok index af3c1505032..07224556f86 100644 --- a/src/mpl/test/halos1.defok +++ b/src/mpl/test/halos1.defok @@ -184,7 +184,7 @@ TRACKS X 190 DO 282 STEP 3200 LAYER metal10 ; TRACKS Y 140 DO 282 STEP 3200 LAYER metal10 ; COMPONENTS 152 ; - MACRO_1 HM_100x100_1x1 + FIXED ( 15000 20210 ) S + HALO 5000 10000 15000 20000 ; - - MACRO_2 HM_100x100_1x1 + FIXED ( 140 242530 ) S ; + - MACRO_2 HM_100x100_1x1 + FIXED ( 140 242250 ) S ; - _001_ DFF_X1 + PLACED ( 316930 334800 ) N ; - _002_ DFF_X1 + PLACED ( 316930 334800 ) N ; - _003_ DFF_X1 + PLACED ( 316930 334800 ) N ; @@ -338,7 +338,7 @@ COMPONENTS 152 ; END COMPONENTS BLOCKAGES 2 ; - PLACEMENT + SOFT + COMPONENT MACRO_1 RECT ( 0 210 ) ( 220000 230210 ) ; - - PLACEMENT + SOFT + COMPONENT MACRO_2 RECT ( 0 242390 ) ( 200280 442670 ) ; + - PLACEMENT + SOFT + COMPONENT MACRO_2 RECT ( 0 242110 ) ( 200280 442390 ) ; END BLOCKAGES NETS 0 ; END NETS diff --git a/src/mpl/test/halos4.defok b/src/mpl/test/halos4.defok index af3c1505032..07224556f86 100644 --- a/src/mpl/test/halos4.defok +++ b/src/mpl/test/halos4.defok @@ -184,7 +184,7 @@ TRACKS X 190 DO 282 STEP 3200 LAYER metal10 ; TRACKS Y 140 DO 282 STEP 3200 LAYER metal10 ; COMPONENTS 152 ; - MACRO_1 HM_100x100_1x1 + FIXED ( 15000 20210 ) S + HALO 5000 10000 15000 20000 ; - - MACRO_2 HM_100x100_1x1 + FIXED ( 140 242530 ) S ; + - MACRO_2 HM_100x100_1x1 + FIXED ( 140 242250 ) S ; - _001_ DFF_X1 + PLACED ( 316930 334800 ) N ; - _002_ DFF_X1 + PLACED ( 316930 334800 ) N ; - _003_ DFF_X1 + PLACED ( 316930 334800 ) N ; @@ -338,7 +338,7 @@ COMPONENTS 152 ; END COMPONENTS BLOCKAGES 2 ; - PLACEMENT + SOFT + COMPONENT MACRO_1 RECT ( 0 210 ) ( 220000 230210 ) ; - - PLACEMENT + SOFT + COMPONENT MACRO_2 RECT ( 0 242390 ) ( 200280 442670 ) ; + - PLACEMENT + SOFT + COMPONENT MACRO_2 RECT ( 0 242110 ) ( 200280 442390 ) ; END BLOCKAGES NETS 0 ; END NETS diff --git a/src/mpl/test/orientation_improve1.defok b/src/mpl/test/orientation_improve1.defok index 1db1c3a2264..431297f531c 100644 --- a/src/mpl/test/orientation_improve1.defok +++ b/src/mpl/test/orientation_improve1.defok @@ -97,13 +97,13 @@ TRACKS Y 140 DO 282 STEP 3200 LAYER metal9 ; TRACKS X 190 DO 282 STEP 3200 LAYER metal10 ; TRACKS Y 140 DO 282 STEP 3200 LAYER metal10 ; COMPONENTS 1 ; - - MACRO_1 HM_100x100_1x1 + FIXED ( 21540 21330 ) S ; + - MACRO_1 HM_100x100_1x1 + FIXED ( 21540 21050 ) S ; END COMPONENTS PINS 1 ; - io_1 + NET io_1 + DIRECTION INPUT + USE SIGNAL ; END PINS BLOCKAGES 1 ; - - PLACEMENT + SOFT + COMPONENT MACRO_1 RECT ( 21400 21190 ) ( 221680 221470 ) ; + - PLACEMENT + SOFT + COMPONENT MACRO_1 RECT ( 21400 20910 ) ( 221680 221190 ) ; END BLOCKAGES NETS 1 ; - io_1 ( PIN io_1 ) ( MACRO_1 I1 ) + USE SIGNAL ; diff --git a/src/mpl/test/orientation_improve3.defok b/src/mpl/test/orientation_improve3.defok index 2150dc3cd64..3b4ea6abdf9 100644 --- a/src/mpl/test/orientation_improve3.defok +++ b/src/mpl/test/orientation_improve3.defok @@ -97,7 +97,7 @@ TRACKS Y 140 DO 282 STEP 3200 LAYER metal9 ; TRACKS X 190 DO 282 STEP 3200 LAYER metal10 ; TRACKS Y 140 DO 282 STEP 3200 LAYER metal10 ; COMPONENTS 1 ; - - MACRO_1 HM_100x100_1x1 + FIXED ( 21540 21330 ) S ; + - MACRO_1 HM_100x100_1x1 + FIXED ( 21540 21050 ) S ; END COMPONENTS PINS 1 ; - io_1 + NET io_1 + DIRECTION INPUT + USE SIGNAL @@ -106,7 +106,7 @@ PINS 1 ; + FIXED ( 239930 39900 ) N ; END PINS BLOCKAGES 1 ; - - PLACEMENT + SOFT + COMPONENT MACRO_1 RECT ( 21400 21190 ) ( 221680 221470 ) ; + - PLACEMENT + SOFT + COMPONENT MACRO_1 RECT ( 21400 20910 ) ( 221680 221190 ) ; END BLOCKAGES NETS 1 ; - io_1 ( PIN io_1 ) ( MACRO_1 I1 ) + USE SIGNAL ;