Skip to content

rsz: add default max fanout backstop - #11197

Draft
minjukim55 wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:secure-rsz-default-max-fanout-50
Draft

rsz: add default max fanout backstop#11197
minjukim55 wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:secure-rsz-default-max-fanout-50

Conversation

@minjukim55

Copy link
Copy Markdown
Contributor

Summary

sta::checkFanout reports no limit when neither the liberty libraries nor the SDC constrain fanout, so rsz skipped fanout repair entirely and high fanout nets stayed on one driver.

Resizer::checkFanout passes an existing liberty or SDC limit through and otherwise falls back on a default of 50 load pins. It counts load pins itself, so no OpenSTA change is needed and libraries with no fanout load data are covered too. It skips the pins OpenSTA leaves out of the check: non drivers, constants, disabled pins, ideal clocks.

Also fixes the unbuffer move, which tested limit > 0.0 while sta::checkFanout reports INF for an unconstrained pin, leaving its kBufferRemovalMaxFanout default of 10 unreachable.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

Impact

Designs with no max fanout limit now get fanout trees. On upf_aes: 3 fanout violations repaired, 44 buffers against 4, gpl timing-driven repair_design area recovery -24.70% -> -22.92%. On repair_slew8 (250 load reset net): area +2.8% against +2.2%, one -0.04 max slew violation left.

Verification

  • Full ctest: 8310/8311. The one failure, psm.report_writers_require_solution, is a test isolation issue unrelated to this change (its own previous run leaves the file it asserts absent); it passes with a clean results dir.
  • Goldens regenerated where the backstop changes results; new repair_fanout9 covers a library with no fanout load data.

Related Issues

N/A

sta::checkFanout reports no fanout limit when neither the liberty libraries
nor the sdc constrain fanout, and rsz then skipped fanout repair entirely.
High fanout nets, for example a synchronous reset network, were left on a
single driver for repair_design to attack through max slew and max cap
alone, which splits the wire instead of building a fanout tree.

Add Resizer::checkFanout, which passes an existing liberty or sdc limit
through untouched and otherwise falls back on a default of 50 load pins, so
those nets still get a buffer tree.  It counts load pins itself rather than
liberty fanout load, so the backstop applies to libraries with no fanout
load data as well, and it skips the pins OpenSTA leaves out of the fanout
check: non drivers, constants, disabled pins and ideal clocks.  repairNet,
the gain buffering round and the rebuffer tree limit all go through it.

The unbuffer move tested its own limit with `limit > 0.0`, but
sta::checkFanout reports an infinite limit rather than a zero one when
nothing constrains the pin, so every removal passed the guard and the
kBufferRemovalMaxFanout default of 10 was unreachable.  Test the limit
against INF so the default applies again.

Signed-off-by: Minju Kim <mkim@precisioninno.com>
The flow tests in test/ are not part of ctest, so the backstop's effect on
them was missed.  Six of the thirteen fail their metric limits: the backstop
buffers high fanout nets that used to stay on one driver, which trades setup
slack and buffer count for slew and capacitance slack.

Failing gates, old value -> new value (limit):

  aes_sky130hd    RSZ::repair_design_buffer_count   580 -> 932   (696)
                  DRT::worst_slack_max            -1.357 -> -1.886 (-1.731)
  aes_sky130hs    RSZ::repair_design_buffer_count   722 -> 980   (866)
                  DRT::max_slew_slack               5.8% -> -4.5%  (0%)
                  DRT::max_capacitance_slack        6.5% -> -6.4%  (0%)
  aes_asap7       RSZ::max_slew_slack               0.2% -> -16.9% (0%)
                  DRT::worst_slack_max            -108.3 -> -242.7 (-148.3)
                  DRT::max_slew_slack              -9.5% -> -79.2% (-11.4%)
  jpeg_sky130hd   RSZ::repair_design_buffer_count   295 -> 622   (354)
                  DRT::max_capacitance_slack      -10.0% -> -12.4% (-12.0%)
  jpeg_sky130hs   RSZ::repair_design_buffer_count   538 -> 770   (645)
                  DRT::max_capacitance_slack       11.0% -> -0.9%  (0%)
  ibex_sky130hs   DRT::ANT::errors                    0 -> 1     (0)

The other direction on the same designs: DRT antenna errors drop 1 -> 0 on
aes_sky130hd, aes_sky130hs, jpeg_sky130hd and jpeg_sky130hs, DRV slack
improves on aes_sky130hd (max slew -26.6% -> -11.4%, max cap -28.8% ->
-13.0%), jpeg_sky130hs (max slew -47.6% -> 0.1%) and ibex_sky130hs (max slew
-51.2% -> 1.2%, max cap -51.4% -> 0.0%), and design area drops on the aes and
ibex runs.  gcd on all four platforms, aes_nangate45, ibex_sky130hd and
tinyRocket_nangate45 still pass untouched.

Two of these are regressions the limits now accept rather than golden churn:
aes_asap7 loses 134 ps of routed setup slack and ibex_sky130hs gains one
antenna violation.

Regenerated with save_flow_metrics and save_flow_metrics_limits; all thirteen
flow tests pass afterwards.

Signed-off-by: Minju Kim <mkim@precisioninno.com>
@minjukim55 minjukim55 self-assigned this Aug 20, 2026

@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 introduces a default fanout limit backstop of 50 load pins in the resizer when no liberty or SDC constraints are present. It implements Resizer::checkFanout to apply this limit, updates RepairDesign and Rebuffer to utilize the new check, and adds a test case (repair_fanout9) to verify the behavior. Metric updates across several test cases reflect the new buffering and timing results. There are no review comments, so no feedback is provided.

@maliberty

Copy link
Copy Markdown
Member

@oharboe fyi

@oharboe

oharboe commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

My main concern is that we're missing tests that create a gradient towards better policies here.

I asked agy and it says that repair and buffer insertion is part of the rsz package and that resizing/sizing is a bit of a historical name and that rsz really is a post placement optimization engine.

Matt reverted(or will revert) a naive policy with an upper bound of buffering that agy came up with when I asked it for fix, and I thought sounded plausible and merited some consideration, after it observed that a design I worked on added more buffering because wires got longer because it was harder to find places for buffers, which added more wiring, which added more buffering... This ran for so long that I considered it "stuck".

The test case I was working on had lots of macros and a large die and paths that crossed the die, much like MegaBOOM on asap7. asap7 has MUCH higher wire resistance(or should at 7nm) than sky130hd, so a test case for asap7 is qualitatively different w.r.t. algorithms than sky130hd test-cases.

@oharboe

oharboe commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Regarding testing, I don't think ORFS, nor OpenROAD contains an asap7 design with a large die, macros where wires have to route around and very little space left for buffers. I think synthetic test case of this kind could have some value if it could be fast.

I imagine that any sufficiently well covered rsz policy in ORFS will make some things worse and some better, rarely does a new algorithm dominate all other implementations on the pareto front, so my prior is that ORFS lacks designs that test this new policy.

@oharboe

oharboe commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

I have a hard time seeing how "golden"(rules-base.json, .ok/.okdwf or "testing that it is working as implemented") could help test a policy that handles large global placement problems where we want a gradient towards improving the pareto front of the global placement algorithm.

I see you ran into aes_upf. I tried to convert aes_upf to testing intent #11152, but it had a run-in with Gemini nits, shot my self in the foot, so I closed it until I would have some time to do it properly.

C++ unit-tests is a mechanism, they too can "test what is implemented", rather than test intent. So, I don't think C++ unit-tests, the mechanism, will magically test that we are improving the global placement algorithm. A .tcl test could equally well test improvements in global placement aglorithms.

I think that great tests document the intent, verify against a specification and creates a gradient to improve the product, move the pareto front, when comparing against the merge-base.

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.

3 participants