The-OpenROAD-Project / The-OpenROAD-Project/OpenROAD
Feature request: make write_macro_placement a complete, replayable record of macro placement
@AcKoucher is already working on this.
Since Sep 15, 2026.
- Dominant language
- Verilog
- Stars
- 3.1k
- Forks
- 1k
- Avg merge
- 2d 23h
- Merged PRs (30d)
- 136
Description
Feature request: make write_macro_placement a complete, replayable record of macro placement
Motivation
I thought it already did this, but there are some gaps.
We would like to use macro placement as a least-authority step in a flow: instead of
handing the next stage a whole new .odb written by the macro placer, run
rtl_macro_placer, dump its result to a Tcl file, and then have the next stage re-read
the floorplan .odb (the same one macro placement read) and source that Tcl file. The
macro placer's .odb becomes a debug artifact only.
The value: the entire influence of macro placement on the flow is a short, reviewable,
diffable text file, instead of an opaque database that the placer had unrestricted write
access to. It also makes macro placement trivially overridable and reproducible.
Today write_macro_placement almost supports this, but not quite.
What is missing
Replaying the output of write_macro_placement onto the pre-macro-placement database
does not reproduce the database that rtl_macro_placer leaves behind. Concretely, using
src/mpl/test/testcases/io_constraints4.def (1 macro, 400 std cells):
A. read_def -> rtl_macro_placer -halo_width 1 -halo_height 1 -> write_def
B. read_def -> source the write_macro_placement output -> write_def
diff A B shows exactly two classes of difference:
-
The soft halo blockage is not in the Tcl file. A contains
BLOCKAGES 1 ; - PLACEMENT + SOFT + COMPONENT MACRO_1 RECT ( 0 30 ) ( 204000 200310 ) ; END BLOCKAGESB has no
BLOCKAGESsection. These are created in
HierRTLMP::commitMacroPlacementToDb()(src/mpl/src/hier_rtlmp.cpp) and in
HierRTLMP::blockMacroChannels(). They are load bearing:gplblocks the sites they
cover (src/gpl/src/placerBase.cpp,dbBlockageloop), so dropping them silently
changes placement quality — no error, no warning.They also cannot be reconstructed by the caller from the halo it requested. In the run
above the halo is 2000 DBU in x but 140 DBU in y, because withuse_full_halo_false
(the default)ClusteringEngine::buildMacroHalo()applies the requested halo only on
boundaries where the master has signal pins and uses a minimum spacing elsewhere. The
resulting rectangle is per-macro, asymmetric, and a function of mpl-internal pin
analysis. -
Temporary std cell placement is left in the database. In A all 400 std cells are
+ PLACEDand stacked on a single point:- _001_ DFF_X1 + PLACED ( 248680 123200 ) N ; - _002_ DFF_X1 + PLACED ( 248680 123200 ) N ; (x400, same coordinates)In B they are untouched/unplaced. This comes from
HierRTLMP::generateTemporaryStdCellsPlacement(), scratch state for the
orientation-improvement step that is never reverted. This is presumably not intended
to be part of the result, and we would be happy to see it not reproduced — we
mention it only because it means the two databases differ.
The macro instances themselves round-trip perfectly: position, orientation and
LOCKED/FIXED status are byte identical between A and B, and re-snapping an
already-snapped macro is a fixed point. So place_macro is doing its job; it is only the
non-macro side effects that are unrecorded.
Proposed change
Make the file written by write_macro_placement (and by
rtl_macro_placer -write_macro_placement) a complete description of what the macro placer
did to the database, by additionally emitting the soft blockages it created, e.g.:
place_macro -macro_name {MACRO_1} -location {1 0.085} -orientation R180
create_blockage -region {0 0.015 102 100.155} -soft -inst MACRO_1
create_blockage already exists with -soft and -inst (src/odb/src/swig/tcl/odb.tcl),
so no new command is needed.
Ideally, with that in place the following would hold as a regression test:
reading the pre-placement DB and sourcing the written Tcl yields the same DB as running
rtl_macro_placer, modulo the temporary std cell positions.
Optionally, clearing the temporary std cell placement at the end of HierRTLMP::run()
would make that "modulo" unnecessary.
Related
https://github.com/The-OpenROAD-Project/OpenROAD/issues/11279 reports the same two side
effects — the temporary std cell placement and the soft blockages — from the opposite
direction: when every macro is fixed, rtl_macro_placer skips the pipeline and produces
neither, so a placement re-injected via place_macro diverges from the run that
generated it (~2% achieved period on a macro-array design).
Both issues are asking for the same property — a macro placement handed over as text
should behave like the run that produced it — and they can be resolved together:
- emit the soft blockages into the written Tcl (this issue), so the replay recreates them
regardless of which code path ran, and - clear the temporary std cell placement at the end of
HierRTLMP::run(), so the
generating run and the injected run agree on it by both not having it.
Note that #11279's suggested fix goes the other way for the std cells — make the
all-fixed path also produce them. Either convention works for us; what matters is that
the generating run and the replay agree.
Alternatives considered
Reconstructing the halo blockages in the flow from the requested halo values. This does
not work, per the pin-aware halo behaviour described above, and it would duplicate
mpl-internal logic in a downstream script.
🤖 Generated with Claude Code
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.