Skip to content

mpl: check core containment against the post-orientation footprint - #11155

Open
sfmth wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
sfmth:master
Open

mpl: check core containment against the post-orientation footprint#11155
sfmth wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
sfmth:master

Conversation

@sfmth

@sfmth sfmth commented Aug 16, 2026

Copy link
Copy Markdown

Summary

MacroPlacer::placeMacro built its core-containment rectangle from
inst->getBBox() and raised MPL-0034 at src/mpl/src/rtl_mp.cpp:109, but did
not call inst->setOrient(orientation) until 24 lines later. Since getBBox()
returns the bounding box cached for the instance's current orientation, any
request whose orientation differs in right-angle-ness from the current one was
validated against the wrong footprint — the four right-angle orientations
(R90, R270, MXR90, MYR90) swap width and height.

This computes the width and height the macro will actually have once
orientation is applied and validates that instead. The comparison is made
against the instance's current orientation rather than assuming R0, so a macro
that has already been rotated — for example by an earlier attempt in a caller's
retry loop, which may reset placement status without resetting orientation — is
handled correctly rather than double-swapped.

The existing comment two lines below the check ("Orientation must be set before
location so we don't end up flipping and misplacing the macro") shows the
ordering of setOrient/setLocation was deliberate; only the containment test
was left above both.

Type of Change

  • Bug fix

Impact

place_macro now validates core containment against the footprint the macro will
have after the requested orientation is applied, rather than the one it
currently has. Only calls that change the instance's right-angle-ness are
affected: placements at R0/R180/MX/MY on an unrotated instance take the
identical path as before, square macros are unaffected, and the existing mpl
suite passes unchanged (36/36). For the four right-angle orientations two
behaviours change, in opposite directions. A legal placement of a rotated
non-square macro is no longer rejected — previously it was measured against its
unrotated footprint, so a macro that fits only when rotated was refused with
MPL-0034; the new test is exactly this case, a 100×400 µm macro placed at R90
in a 499.89×198.80 µm core. Symmetrically, a rotated macro that genuinely does
not fit is now caught, where before the swapped dimensions could let it pass the
check and be placed and LOCKED outside the core. That second direction is the
one to be aware of when upgrading: a flow that previously appeared to succeed may
now report MPL-0034, but the placement it produced was already out of bounds and
simply went undetected.

Verification

  • I have verified that the local build succeeds (./etc/Build.sh).
    Built at cbc7678e45 with the equivalent direct invocation:
    cmake -B build -G Ninja && ninja -C build openroad (Release, gcc).
  • I have run the relevant tests and they pass.
    src/mpl/test/./regression36/36 mpl tests pass, including the new one.
  • My code follows the repository's formatting guidelines.
    clang-format -i src/mpl/src/rtl_mp.cpp applied.
  • I have included tests to prevent regressions.
    place_macro_rotated, registered in both CMakeLists.txt and BUILD.
  • I have signed my commits (DCO).

Evidence the test catches the bug

The same test, same testcase, against a binary built from unpatched source versus
the patched one:

binary result
unpatched (26Q2-1164-g08f67ee5ec) aborts at the R90 call — [ERROR MPL-0034] Cannot place macro at (20, 20) (120, 420), outside of the core (0, 0) (499.89, 198.8).
patched (26Q3-1297-gcbc7678e45) [INFO MPL-0035] Macro macro placed. Bounding box (20.000um, 20.000um), (420.000um, 120.000um). Orientation R90

The rejected rectangle in the failing case, (120, 420), is the macro's
unrotated 100×400 footprint offset by the requested origin — the bug is visible
in the error message itself. The accepted rectangle is the rotated 400×100
footprint, which fits the core.

The test also asserts that the same origin is still rejected at R0, where
the macro genuinely does not fit, so the change cannot silently over-correct in
the permissive direction.

Related Issues

None filed — reporting directly via this PR. The defect is reachable from the
public place_macro command for any caller that passes a right-angle orientation
for a non-square macro; HierRTLMP::placeMacros uses a separate commit path and
is unaffected.

@sfmth
sfmth requested a review from a team as a code owner August 16, 2026 04:36
@sfmth
sfmth requested a review from AcKoucher August 16, 2026 04:36

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Welcome to OpenROAD! Thanks for opening your first PR.
Before we review:

Please ensure:

  • CI passes
  • Code is properly formatted
  • Tests are included where applicable
    A maintainer will review shortly!

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the macro containment validation in MacroPlacer::placeMacro to use the correct footprint of the macro after the requested orientation is applied. Specifically, it swaps the width and height if the requested orientation differs in right-angle rotation from the current orientation, preventing legal rotated placements from being rejected and illegal ones from being accepted. A new integration test, place_macro_rotated, has been added to verify this fix. There are no review comments, and I have no additional feedback to provide.

sfmth added 2 commits August 16, 2026 16:48
place_macro validated the core-containment rectangle using the
instance's
bounding box *before* the requested orientation was applied. The four
right-angle orientations (R90, R270, MXR90, MYR90)
swap width and height, so whenever the requested orientation differed
from the current one in right-angle-ness the check used the wrong
footprint: it rejected legal placements of rotated macros with
MPL-0034, and symmetrically would accept ones that do not fit.

Compute the width and height the macro will actually have once
`orientation` is applied, and validate that instead. The comparison is
made against the instance's current orientation rather than assuming
R0, so a macro that has already been rotated is handled correctly too.

The new test places a 100x400um macro at R90 in a 499.89x198.80um
core, where the rotated footprint (400x100) fits and the unrotated one
(100x400) does not. It also asserts the same origin is still rejected
at R0, so the fix cannot silently over-correct.

    Signed-off-by: Farhad Modaresi <sfmth0@gmail.com>
The new test was added to COMPULSORY_TESTS but not to the per-test
resource dict. src/mpl/test/BUILD does not glob the testcases/
directory -- the filegroup globs only `test_name + ".*"`, which picks up
the .tcl, .ok and .defok, so each test's LEF and DEF have to be listed
explicitly.

The Bazel target therefore resolved and ran without
testcases/place_macro_rotated.{lef,def}, while the CMake build passed
because it collects test inputs differently.

bazel query deps(//src/mpl/test:place_macro_rotated_resources) now lists
all six required inputs, and
bazel test //src/mpl/test:place_macro_rotated-tcl_test passes.

Signed-off-by: Farhad Modaresi <sfmth0@gmail.com>

@AcKoucher AcKoucher left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, @sfmth! Thanks for catching this.

Comment thread src/mpl/src/rtl_mp.cpp
Comment on lines +104 to +119

// The containment check below must use the footprint the macro will have
// once `orientation` is applied, not the one it has right now. The four
// right-angle orientations swap width and height, so validating the
// pre-setOrient bbox rejects legal placements of rotated macros -- and
// symmetrically accepts illegal ones -- whenever the requested orientation
// differs from the current one in right-angle-ness.
int width = inst->getBBox()->getDX();
int height = inst->getBBox()->getDY();
if (orientation.isRightAngleRotation()
!= inst->getOrient().isRightAngleRotation()) {
std::swap(width, height);
}

const int x2 = x1 + width;
const int y2 = y1 + height;

@AcKoucher AcKoucher Aug 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just setOrient before getting the coordinates to avoid the check.

fixed_covers
macros_without_pins1
unfixed_cells_dont_fit_in_core
place_macro_rotated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more descriptive name would be place_macro_with_right_angle_rotation

Comment on lines +15 to +24
# The regression: this is legal at R90 and must be accepted.
place_macro -macro_name macro -location {20 20} -orientation R90

# The guard against over-correcting: the same origin is genuinely outside the
# core at R0, and must still be rejected.
if { [catch { place_macro -macro_name macro -location {20 20} -orientation R0 } msg] } {
puts "R0 correctly rejected"
} else {
puts "ERROR: R0 placement should have been rejected"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a single place_macro call with the macro on R90 successfully passing is fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants