mpl: keep snapped macros inside the core - #11275
Conversation
The Snapper picked the track (or manufacturing-grid) position closest to the pin center, rounding up when in doubt. For a macro at the top or right core edge this moved the macro bounding box slightly past the core boundary. The residue is invisible to the flow that produced it, but place_macro rejects such a position with MPL-0034, so the file written by -write_macro_placement did not round-trip: the placer's own winner could not be re-injected with MACRO_PLACEMENT_TCL. Constrain the track-alignment search to positions that keep the macro inside the core, and nudge the macro inward by whole manufacturing-grid steps when no aligned position qualifies (new warning MPL-0078). Fixed macros are untouched as before. The regenerated goldens of boundary_push1, fixed_macros2, halos5 and orientation_improve1/3 all contained placer-produced macros poking past the core boundary; their macros now land inside the core, track-aligned one pitch inward. The new macro_placement_round_trip1 test checks the write_macro_placement -> place_macro round-trip lands every macro exactly where the placer left it. Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
There was a problem hiding this comment.
Code Review
This pull request ensures that macro placement remains within the core boundary during snapping operations by introducing validation and adjustment helpers (macroStaysInCore and nudgeMacroIntoCore). It also updates affected test outputs and adds new unit and integration tests. The review feedback suggests adding defensive checks in nudgeMacroIntoCore to handle degenerate cores and prevent potential division-by-zero crashes.
| const odb::Rect core = inst_->getBlock()->getCoreArea(); | ||
| const odb::Rect bbox = inst_->getBBox()->getBox(); | ||
| const int manufacturing_grid | ||
| = inst_->getDb()->getTech()->getManufacturingGrid(); |
There was a problem hiding this comment.
To prevent potential runtime issues, we should add defensive checks in nudgeMacroIntoCore:
- Degenerate Core Check: If the core area is degenerate (e.g., before rows are created), we should return early to avoid unnecessary or incorrect shifting, matching the behavior in
macroStaysInCore. - Division by Zero Guard: If
manufacturing_gridis0or negative (which can happen with incomplete technology data), the rounding-up division will cause a division-by-zero crash. Defaulting to1(the minimum database unit) avoids this.
| const odb::Rect core = inst_->getBlock()->getCoreArea(); | |
| const odb::Rect bbox = inst_->getBBox()->getBox(); | |
| const int manufacturing_grid | |
| = inst_->getDb()->getTech()->getManufacturingGrid(); | |
| const odb::Rect core = inst_->getBlock()->getCoreArea(); | |
| if (core.dx() == 0 || core.dy() == 0) { | |
| return; | |
| } | |
| const odb::Rect bbox = inst_->getBBox()->getBox(); | |
| int manufacturing_grid | |
| = inst_->getDb()->getTech()->getManufacturingGrid(); | |
| if (manufacturing_grid <= 0) { | |
| manufacturing_grid = 1; | |
| } |
|
Closing per our downstream plan: carrying this as a patch in bazel-orfs while mpl churns, will re-upstream once it settles. Tracked with a minimal reproducer in #11278 (which also notes the place_macro self-overlap observation). |
The Snapper picks the track (or manufacturing-grid) position closest to
the pin center, rounding up when in doubt. For a macro at the top or
right core edge this moves the macro bounding box slightly past the core
boundary. The residue is invisible to the flow that produced it, but
place_macro rejects such a position with MPL-0034 — so the file written
by -write_macro_placement does not round-trip: the placer's own winner
cannot be re-injected via MACRO_PLACEMENT_TCL.
This PR constrains the track-alignment search to positions that keep the
macro inside the core, and nudges the macro inward by whole
manufacturing-grid steps when no aligned position qualifies (new warning
MPL-0078). Fixed macros are untouched, as before.
Note on goldens: the regenerated goldens of boundary_push1,
fixed_macros2, halos5 and orientation_improve1/3 all contained
placer-produced macros poking past the core boundary (verified by
comparing each macro bbox against dbBlock::getCoreArea()); their macros
now land inside the core, track-aligned one pitch inward.
Tests: two new TestSnapper unit cases (manufacturing-grid and
track-aligned containment) and macro_placement_round_trip1, which checks
write_macro_placement -> place_macro lands every macro exactly where the
placer left it.
🤖 Generated with Claude Code