rsz: clamp buffer locations to core area during insertion#10141
Conversation
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! 👍" |
|
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! 👍" |
2 similar comments
|
clang-tidy review says "All clean, LGTM! 👍" |
|
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);
}
There was a problem hiding this comment.
makeInstance -> setLocation() path don't call placeBufferLocation() so rsz module's clamp() is still needed.
sta::Instance* Resizer::makeInstance(sta::LibertyCell* cell,
const char* name,
sta::Instance* parent,
const odb::Point& loc,
const odb::dbNameUniquifyType& uniquify)
{
There was a problem hiding this comment.
OK. I see.
- Only
Resizer::setLocation()can see the core area (Resizer::core_attribute), andResizer::setLocation()cannot be called inodb::dbInsertBuffer::placeBufferAtLocation().
|
I have no further comments beyond @jhkim-pii |
31e0b05 to
b9e31bd
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
b9e31bd to
5f97307
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
| # Tight core: (10, 10) to (90, 90) microns within a 200x200 die. | ||
| initialize_floorplan -site $site \ | ||
| -die_area {0 0 200 200} \ | ||
| -core_area {10 10 90 90} |
There was a problem hiding this comment.
If the new test case provides repair_design_outside_core.def, this regression can simply read the def file w/o initialize_floorplan call.
How about reducing the test case further?
| y = core_.yMax() - height; | ||
| buffer_moved_into_core_ = true; | ||
| } | ||
| const odb::Point loc = clampLocToCore(pt, db_inst->getMaster()); |
|
|
||
| db_inst->setPlacementStatus(dbPlacementStatus::PLACED); | ||
| db_inst->setLocation(x, y); | ||
| odb::Point Resizer::clampLocToCore(const odb::Point& loc, |
| void Resizer::warnBufferMovedIntoCore() | ||
| { | ||
| if (buffer_moved_into_core_) { | ||
| logger_->warn(RSZ, 77, "some buffers were moved inside the core."); |
There was a problem hiding this comment.
Removed unnecessary comment (It’s only implemented in code, but it’s not actually written to the log)
| --------------------------------------------------------------------- | ||
| 0 | +0.0% | 0 | 0 | 0 | 24 | ||
| [DEBUG RSZ-buffer_clamp] fanout1 clamped to core (51780, 1230) -> (51780, 22400) | ||
| [DEBUG RSZ-buffer_clamp] fanout2 clamped to core (1780, 101230) -> (20140, 101230) |
| // 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.
makeInstance -> setLocation() path don't call placeBufferLocation() so rsz module's clamp() is still needed.
sta::Instance* Resizer::makeInstance(sta::LibertyCell* cell,
const char* name,
sta::Instance* parent,
const odb::Point& loc,
const odb::dbNameUniquifyType& uniquify)
{
Squashed from secure-rsz-instance-outside-core (13 commits) and rebased onto current ORFS master OR pointer (0504146) to isolate the clamp change from unrelated rsz refactors (ServiceRegistry port, OpenSTA sync, dpl-negotiation-fixes, etc.) that landed on master during development. Includes: - clampLocToCore() utility - insertBufferPostProcess folds setLocation with clamp - per-buffer info log replaces summary warning - repair_design_outside_core test - aes_sky130hd metrics limits update Signed-off-by: minjukim55 <mkim@precisioninno.com>
Replace the initialize_floorplan + manual setLocation loop with a read_def -floorplan_initialize call, per Matt's review guidance that test cases should not invoke high-level commands (initialize_floorplan, placement, routing) when those steps are unrelated to the buggy point. Also reduce the load count from 10 to 6 (just over the max_fanout 5 threshold), keeping coverage of both the fanout buffer and wire repeater clamp paths while shrinking the script from ~95 to ~21 lines. The verification loop is removed in favor of relying on the .ok snapshot diff to detect regressions in clamp behavior. Signed-off-by: minjukim55 <mkim@precisioninno.com>
5f97307 to
7ac212c
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
| read_verilog repair_design_outside_core.v | ||
| link_design outside_core | ||
| read_def -floorplan_initialize repair_design_outside_core.def |
There was a problem hiding this comment.
It doesn't matter much for such a small test case but you don't really need the verilog at all. You could just load a def file.
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