rsz: clamp buffer locations to core area during insertion#10141
rsz: clamp buffer locations to core area during insertion#10141openroad-ci wants to merge 13 commits intoThe-OpenROAD-Project:masterfrom
Conversation
Buffers inserted by RSZ (repair_design, repair_hold, repair_setup, recover_power) could be placed outside the core area when Steiner points fall beyond the core boundary. This causes legalization failures and DRC violations. Add clampLocToCore() that clamps buffer coordinates to the core bounding box, accounting for cell width/height. Apply it in both the makeBuffer (setLocation) and insertBuffer (ODB) paths. Emit RSZ-0077 warning when any buffer was moved, and a debug-level message with per-buffer coordinates (set_debug_level RSZ buffer_move 1). Add repair_design_outside_core regression test that verifies all inserted buffers stay within core. Fixes The-OpenROAD-Project-private/OpenROAD-flow-scripts#1622 Signed-off-by: minjukim55 <mkim@precisioninno.com>
Signed-off-by: minjukim55 <mkim@precisioninno.com>
Signed-off-by: minjukim55 <mkim@precisioninno.com>
RepairHold was missing the flag reset and warnBufferMovedIntoCore() call that all other repair passes (RepairDesign, RepairSetup, RecoverPower) already had. This meant hold repair could silently place buffers outside core without warning. Signed-off-by: minjukim55 <mkim@precisioninno.com>
Buffer locations clamped to core area change wire lengths, causing ~1% regression in DRT max_slew_slack and max_capacitance_slack. This is expected QoR impact from correct buffer placement. Signed-off-by: minjukim55 <mkim@precisioninno.com>
The previous RSZ-0077 warning ("some buffers were moved inside the core")
required enabling a debug level to see which buffers were clamped and
where. Replace this with a direct info log from clampLocToCore() that
always reports the master cell, original location, and clamped
location — useful for debugging without needing to rerun with
set_debug_level RSZ buffer_move 1.
Remove the now-unused buffer_moved_into_core_ flag and
warnBufferMovedIntoCore() helper from Resizer, and strip the reset/warn
calls that were sprinkled through RepairDesign, RepairSetup, RepairHold,
RecoverPower, and bufferInputs/bufferOutputs.
Update golden files for the 10 regression tests that previously emitted
the summary warning to instead match the new per-buffer INFO messages.
Signed-off-by: minjukim55 <mkim@precisioninno.com>
Previously the three insertBuffer* wrappers each duplicated a
clamped_loc/effective_loc dance before calling the ODB layer, and the
clamp log message showed the library cell name (e.g. CLKBUF_X1) which
is not actionable when tracing a specific misplaced buffer.
Fold the clamp into setLocation(), which now runs after the ODB insert
returns the instance. This lets the log use the instance name
(e.g. hold5, wire1) and lets clampLocToCore return by value with no
out-parameter.
- clampLocToCore: value-returning, const, no logging
- setLocation: calls clampLocToCore, logs on clamp with instance name
- insertBuffer{AfterDriver,BeforeLoad,BeforeLoads}: drop pre-clamp,
call setLocation(buffer_inst, *loc) after ODB insert succeeds
- Update 10 .ok files: log now reports instance names
Signed-off-by: minjukim55 <mkim@precisioninno.com>
The three insertBuffer* wrappers each repeated the same tail:
if (loc) {
setLocation(buffer_inst, *loc);
}
insertBufferPostProcess(buffer_inst);
Move the if-guard into insertBufferPostProcess, which now takes the
optional location alongside the instance. This removes three copies
of identical glue and keeps "place, then post-process" atomic so a
future contributor cannot inject code between the two steps.
Signed-off-by: minjukim55 <mkim@precisioninno.com>
The timing-driven global_placement invoked in upf_aes.tcl inserts a repair_design buffer at y=966219 whose top edge would extend 619 DBU past core yMax=970000. The new RSZ-0077 info message now records the clamp; sync the golden log. Signed-off-by: minjukim55 <mkim@precisioninno.com>
Lost in the fold-into-postProcess refactor; restore it above each insertBufferPostProcess() call site for readability. Signed-off-by: minjukim55 <mkim@precisioninno.com>
There was a problem hiding this comment.
Code Review
This pull request replaces the manual buffer-moved-into-core warning with an automatic clamping mechanism using a new clampLocToCore method, and adds a corresponding test case. Feedback highlights that the modified signature of insertBufferPostProcess causes compilation errors at several call sites where references are passed instead of pointers, and misses other sites like makeBuffer. To resolve these errors and improve API usability, it is recommended to revert the signature change and have the method retrieve the buffer's location internally, ensuring consistent clamping for all inserted buffers.
| const odb::Point& loc); | ||
|
|
||
| void insertBufferPostProcess(odb::dbInst* buffer_inst); | ||
| void insertBufferPostProcess(odb::dbInst* buffer_inst, const odb::Point* loc); |
There was a problem hiding this comment.
The change to the signature of insertBufferPostProcess to take a pointer const odb::Point* loc introduces several issues:
- Compilation Errors: The call sites updated in Resizer.cc (e.g., line 4910) pass a reference loc to this pointer parameter, which will not compile in C++.
- Incomplete Updates: Other call sites not included in this diff (such as makeBuffer) will also fail to compile as they now lack the required second argument.
- Clamping Policy: Making the location optional via a pointer risks missing clamping for some buffers if nullptr is passed, which contradicts the goal of ensuring all RSZ-inserted buffers land inside the core.
Instead of changing the signature, it is recommended to keep the original void insertBufferPostProcess(odb::dbInst* buffer_inst) and internally call setLocation(buffer_inst, buffer_inst->getLocation()). This ensures all inserted buffers are clamped using their current location without needing to modify any call sites, prioritizing API usability.
| void insertBufferPostProcess(odb::dbInst* buffer_inst, const odb::Point* loc); | |
| void insertBufferPostProcess(odb::dbInst* buffer_inst); |
References
- API usability can be prioritized over strict const-correctness.
|
|
||
| // Legalize the buffer position and update the design area | ||
| insertBufferPostProcess(buffer_inst); | ||
| insertBufferPostProcess(buffer_inst, loc); |
There was a problem hiding this comment.
|
|
||
| // Legalize the buffer position and update the design area | ||
| insertBufferPostProcess(buffer_inst); | ||
| insertBufferPostProcess(buffer_inst, loc); |
|
|
||
| // Legalize the buffer position and update the design area | ||
| insertBufferPostProcess(buffer_inst); | ||
| insertBufferPostProcess(buffer_inst, loc); |
| void Resizer::insertBufferPostProcess(dbInst* buffer_inst, | ||
| const odb::Point* loc) | ||
| { | ||
| // Clamp to core if the caller provided a target location. ODB's | ||
| // insertBuffer* already placed the instance at *loc; setLocation will | ||
| // re-place it if clamping changed the point. | ||
| if (loc) { | ||
| setLocation(buffer_inst, *loc); | ||
| } |
There was a problem hiding this comment.
By reverting the signature change and querying the instance's current location, you ensure that clamping is always applied to any buffer undergoing post-processing, regardless of how it was instantiated. This also fixes the compilation errors at the call sites and ensures that methods like makeBuffer also benefit from the clamping logic. This approach prioritizes API usability over the proposed signature change.
void Resizer::insertBufferPostProcess(dbInst* buffer_inst)
{
// Clamp to core. ODB's insertBuffer* already placed the instance;
// setLocation will re-place it if clamping is required.
setLocation(buffer_inst, buffer_inst->getLocation());
}References
- API usability can be prioritized over strict const-correctness.
|
clang-tidy review says "All clean, LGTM! 👍" |
Add a comment explaining why bufferInputs / bufferOutputs pass loc=nullptr on purpose: port buffers are placed near their BTerm, which can sit at the die edge outside the core, and that is by design -- they should stay adjacent to the port. Makes the `if (loc)` guard self-documenting so future readers do not wonder whether the nullptr case is a bug. Comment-only change; no behavioral diff. Signed-off-by: minjukim55 <mkim@precisioninno.com>
|
Scope clarification — the branch now includes commit Port buffer exception
The three paths through
The "Impact" section of the description should be read as: wire repeaters and other |
|
clang-tidy review says "All clean, LGTM! 👍" |
Make insertBufferPostProcess unconditionally clamp every buffer (including port buffers from bufferInputs/bufferOutputs) to the core area. Previously port buffers passed loc=nullptr and skipped the clamp; they can now land in the die-core gap on designs where die != core, causing the same DPL legalization failures as wire repeaters. Demote the per-buffer RSZ-0077 clamp message from info to debugPrint (category "buffer_clamp", level 1) so it only appears when explicitly enabled with -set_debug rsz buffer_clamp 1. This keeps logs clean while still allowing diagnosis when needed. Updated 18 golden files: removed RSZ-0077 info lines from 12 .ok files and updated 4 .defok files where port buffers now land inside the core. Signed-off-by: minjukim55 <mkim@precisioninno.com>
Enable set_debug_level RSZ buffer_clamp 1 in the test so the per-buffer clamp debug messages appear in the log and get validated against the golden file. Signed-off-by: minjukim55 <mkim@precisioninno.com>
|
clang-tidy review says "All clean, LGTM! 👍" |
1 similar comment
|
clang-tidy review says "All clean, LGTM! 👍" |
| // ODB's insertBuffer* already placed the instance; re-run through | ||
| // setLocation so clampLocToCore can pull it back inside the core if | ||
| // the insertion point landed in the die-core gap. | ||
| setLocation(buffer_inst, buffer_inst->getLocation()); |
There was a problem hiding this comment.
How about replacing the setLocation() call in the following API?
void dbInsertBuffer::placeBufferAtLocation(dbInst* buffer_inst,
const Point& loc,
const char* reason)
{
buffer_inst->setLocation(loc.getX(), loc.getY());
buffer_inst->setPlacementStatus(dbPlacementStatus::PLACED);
dlogPlacedBuffer(buffer_inst, loc, reason);
}
|
I have no further comments beyond @jhkim-pii |
Summary
RSZ-inserted buffers could land outside the core area, in the die-core gap where no placement rows exist, causing downstream DPL legalization failures. Clamp buffer coordinates to the core bounding box at the point of insertion.
Root Cause
The buffers in question are wire repeaters produced by
repair_design(and similarly byrepair_setup/repair_holdrebuffering). The insertion mechanism:SteinerTree::findSteinerPoints).isTopLevelPort(load_pin)returns true for BTerms, so top-level ports become Steiner terminals. Their coordinates come fromdbBTerm::getFirstPinLocation()— which returns the die-boundary pin location, not a core-side coordinate.max_wire_length,RepairDesign::repairNetinserts a repeater by interpolating a point along the segment:setLocation(buf_inst, {buf_x, buf_y})was called without clamping — buffer placed outside core.Verification
Reproduced on a customer design — see The-OpenROAD-Project-private/OpenROAD-flow-scripts#1622 for details. Wire repeaters inserted during the place-resize stage were observed outside the core, all traced to die-edge BTerms feeding the Steiner-point interpolation.
Fix
clampLocToCore()(Resizer.cc, Resizer.hh):(x, y)clamped into[core.xMin, core.xMax - cell.width] × [core.yMin, core.yMax - cell.height]std::maxguard for the upper bound handles the edge case where the core is smaller than the cell (sostd::clampis safe — requireshi >= lo)setLocation()applies the clamp and emits[INFO RSZ-0077] {instance_name} clamped to core ({orig_x}, {orig_y}) -> ({new_x}, {new_y})whenever a buffer is moved — per-buffer detail, always on, instance name lets you grep the DEF directly.Two insertion paths are both covered:
setLocation()path (repair_hold, repair_setup, make_buffer) — clamp insidesetLocation()insertBuffer*path (repair_design wire repeaters) —insertBufferPostProcess(buffer_inst, loc)callssetLocationafter ODB places the buffer, re-placing with the clamped coordinate if neededWhy RSZ-layer (not ODB-layer) clamping
Tempting alternative: clamp inside
dbInsertBuffer::placeBufferAtLocation()(single ODB site). Rejected because:dbInsertBufferis used by non-RSZ callers (e.g.,swap_pins_dont_touch) that don't necessarily want core-clamping.Testing
New test:
repair_design_outside_coreset_max_fanout 5+repair_design -max_wire_length 100triggers wire-repeater insertionUpdated golden files (11 tests) — all exercise paths that now emit per-buffer RSZ-0077 info messages:
repair_hold2/repair_hold8/repair_hold11/repair_hold12repair_hold14repair_slew4/repair_slew12repair_wire11repair_tie5upf_aesrepair_design_outside_coreFull
rszregression: 195/195 pass.Type of Change
Impact
RSZ-0077info messages replace the prior summary warning — instance name + coordinates, more useful for diagnosis, no debug flag needed.Verification
./etc/Build.sh).rszregression (195/195) passes.clang-format,tclfmt,tclintclean.Related Issues
Fixes The-OpenROAD-Project-private/OpenROAD-flow-scripts#1622