Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rsz: Buffering and sizing improvements #6564

Open
wants to merge 35 commits into
base: master
Choose a base branch
from

Conversation

openroad-ci
Copy link
Collaborator

This PR combines a number of related resizer changes impacting QoR:

  • Improvements to gain buffering (repair_design -buffer_gain)
  • Pre-selection of buffers used for repair to filter out slow and delay buffers (except for hold repair)
  • Replacing resize to target slew with a fanout-of-4 rule
  • Restructuring of repair_design to do minimal resizing in response to a violation

It supersedes #6332

povik added 14 commits January 21, 2025 17:43
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
@povik
Copy link
Contributor

povik commented Jan 21, 2025

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

}
size_cin /= nports;
if (size_cin > cin)
cin = size_cin;
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: statement should be inside braces [google-readability-braces-around-statements]

Suggested change
cin = size_cin;
if (size_cin > cin) {
cin = size_cin;
}

float delay_penalty = max_drive_resist * extra_input_cap;

float wlimit = maxLoad(network_->cell(worse)),
blimit = maxLoad(network_->cell(better));
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: Value stored to 'blimit' during its initialization is never read [clang-analyzer-deadcode.DeadStores]

        blimit = maxLoad(network_->cell(better));
        ^
Additional context

src/rsz/src/Resizer.cc:539: Value stored to 'blimit' during its initialization is never read

        blimit = maxLoad(network_->cell(better));
        ^

float delay_penalty = max_drive_resist * extra_input_cap;

float wlimit = maxLoad(network_->cell(worse)),
blimit = maxLoad(network_->cell(better));
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: unused variable 'blimit' [clang-diagnostic-unused-variable]

        blimit = maxLoad(network_->cell(better));
        ^

// TODO: factor out
for (TimingArcSet* arc_set : worse->timingArcSets()) {
TimingRole* role = arc_set->role();
if (role->combinational() && arc_set->from() == win
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: static member accessed through instance [readability-static-accessed-through-instance]

Suggested change
if (role->combinational() && arc_set->from() == win
if (sta::TimingRole::combinational() && arc_set->from() == win


for (TimingArcSet* arc_set : better->timingArcSets()) {
TimingRole* role = arc_set->role();
if (role->combinational() && arc_set->from() == bin
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: static member accessed through instance [readability-static-accessed-through-instance]

Suggested change
if (role->combinational() && arc_set->from() == bin
if (sta::TimingRole::combinational() && arc_set->from() == bin

LibertyCellSeq sizes_by_inp_cap;
sizes_by_inp_cap = buffer_cells_;
sort(sizes_by_inp_cap,
[this](const LibertyCell* buffer1, const LibertyCell* buffer2) {
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: lambda capture 'this' is not used [clang-diagnostic-unused-lambda-capture]

Suggested change
[this](const LibertyCell* buffer1, const LibertyCell* buffer2) {
[](const LibertyCell* buffer1, const LibertyCell* buffer2) {

while (port_iter.hasNext()) {
const LibertyPort* port = port_iter.next();
if (port->direction() == sta::PortDirection::input()) {
cin += port->capacitance();
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage [clang-analyzer-core.uninitialized.Assign]

      cin += port->capacitance();
          ^
Additional context

src/rsz/src/Resizer.cc:1483: Assuming 'inst' is non-null

  LibertyCell* cell = inst ? network_->libertyCell(inst) : nullptr;
                      ^

src/rsz/src/Resizer.cc:1483: '?' condition is true

  LibertyCell* cell = inst ? network_->libertyCell(inst) : nullptr;
                      ^

src/rsz/src/Resizer.cc:1484: Assuming the condition is true

  if (!network_->isTopLevelPort(drvr_pin) && inst && !dontTouch(inst) && cell
      ^

src/rsz/src/Resizer.cc:1484: Left side of '&&' is true

  if (!network_->isTopLevelPort(drvr_pin) && inst && !dontTouch(inst) && cell
      ^

src/rsz/src/Resizer.cc:1484: 'inst' is non-null

  if (!network_->isTopLevelPort(drvr_pin) && inst && !dontTouch(inst) && cell
                                             ^

src/rsz/src/Resizer.cc:1484: Left side of '&&' is true

  if (!network_->isTopLevelPort(drvr_pin) && inst && !dontTouch(inst) && cell
      ^

src/rsz/src/Resizer.cc:1484: Left side of '&&' is true

  if (!network_->isTopLevelPort(drvr_pin) && inst && !dontTouch(inst) && cell
      ^

src/rsz/src/Resizer.cc:1484: Assuming 'cell' is non-null

  if (!network_->isTopLevelPort(drvr_pin) && inst && !dontTouch(inst) && cell
                                                                         ^

src/rsz/src/Resizer.cc:1484: Left side of '&&' is true

  if (!network_->isTopLevelPort(drvr_pin) && inst && !dontTouch(inst) && cell
      ^

src/rsz/src/Resizer.cc:1484: Taking true branch

  if (!network_->isTopLevelPort(drvr_pin) && inst && !dontTouch(inst) && cell
  ^

src/rsz/src/Resizer.cc:1486: 'cin' declared without an initial value

    float cin, load_cap;
          ^

src/rsz/src/Resizer.cc:1491: Assuming the condition is true

    if (load_cap > 0.0 && getCin(cell, cin)) {
        ^

src/rsz/src/Resizer.cc:1491: Left side of '&&' is true

    if (load_cap > 0.0 && getCin(cell, cin)) {
        ^

src/rsz/src/Resizer.cc:1491: Passing value via 2nd parameter 'cin'

    if (load_cap > 0.0 && getCin(cell, cin)) {
                                       ^

src/rsz/src/Resizer.cc:1491: Calling 'Resizer::getCin'

    if (load_cap > 0.0 && getCin(cell, cin)) {
                          ^

src/rsz/src/Resizer.cc:1465: Loop condition is true. Entering loop body

  while (port_iter.hasNext()) {
  ^

src/rsz/src/Resizer.cc:1467: Assuming the condition is true

    if (port->direction() == sta::PortDirection::input()) {
        ^

src/rsz/src/Resizer.cc:1467: Taking true branch

    if (port->direction() == sta::PortDirection::input()) {
    ^

src/rsz/src/Resizer.cc:1468: The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage

      cin += port->capacitance();
          ^

// Includes net parasitic capacitance.
load_cap = graph_delay_calc_->loadCap(drvr_pin, tgt_slew_dcalc_ap_);
if (load_cap > 0.0 && getCin(cell, cin)) {
bool is_buf_inv = cell->isBuffer() || cell->isInverter();
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: Value stored to 'is_buf_inv' during its initialization is never read [clang-analyzer-deadcode.DeadStores]

      bool is_buf_inv = cell->isBuffer() || cell->isInverter();
           ^
Additional context

src/rsz/src/Resizer.cc:1492: Value stored to 'is_buf_inv' during its initialization is never read

      bool is_buf_inv = cell->isBuffer() || cell->isInverter();
           ^

// Includes net parasitic capacitance.
load_cap = graph_delay_calc_->loadCap(drvr_pin, tgt_slew_dcalc_ap_);
if (load_cap > 0.0 && getCin(cell, cin)) {
bool is_buf_inv = cell->isBuffer() || cell->isInverter();
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: unused variable 'is_buf_inv' [clang-diagnostic-unused-variable]

      bool is_buf_inv = cell->isBuffer() || cell->isInverter();
           ^

Comment on lines 15 to 17
Differences found at line 95.
- i1-179 BUF_X16 + PLACED ( 0 3580000 ) N ;
- i1-179 BUF_X8 + PLACED ( 0 3580000 ) N ;
Copy link
Member

Choose a reason for hiding this comment

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

There should be no diffs in the ok file

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, missed that, will fix here and in other places if there are any

Comment on lines 541 to 545
#if 0
if (/* blimit exists */ blimit > 0 && wlimit > blimit) {
return false;
}
#endif
Copy link
Member

Choose a reason for hiding this comment

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

rm unused code

@maliberty
Copy link
Member

09:38:53  The following tests FAILED:
09:38:53  	366 - gpl.convergence01.tcl (Failed)
09:38:53  	367 - gpl.convergence01.py (Failed)
09:38:53  	389 - gpl.simple01-td.tcl (Failed)
09:38:53  	390 - gpl.simple01-td.py (Failed)
09:38:53  	391 - gpl.simple01-td-tune.tcl (Failed)
09:38:53  	392 - gpl.simple01-td-tune.py (Failed)

needs attention

povik added 4 commits January 23, 2025 13:28
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

Signed-off-by: Martin Povišer <povik@cutebit.org>
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

Signed-off-by: Martin Povišer <povik@cutebit.org>
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@precisionmoon
Copy link
Contributor

@maliberty
Copy link
Member

Please resolve the merge conflicts so we can get this in.

Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
@openroad-ci openroad-ci force-pushed the rsz-gain-buffer-changes branch from e737953 to 676e14f Compare February 24, 2025 22:10
@povik
Copy link
Contributor

povik commented Feb 24, 2025

I've resolved the conflicts for now (I think there's more to come from #6728)

Comment on lines 72 to 74
final | -16.3% | 29 | 0 | 0 | 0
final | -114.9% | 75 | 1 | 1 | 0
Copy link
Member

Choose a reason for hiding this comment

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

Are these large negative values expected? Can they really be < 100%?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll investigate

Copy link
Collaborator

Choose a reason for hiding this comment

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

@povik I found the bug here, I'll PR a fix soon

Copy link
Contributor

Choose a reason for hiding this comment

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

OK, so I assume there's a bug unrelated to this PR

Copy link
Collaborator

@gadfort gadfort Feb 25, 2025

Choose a reason for hiding this comment

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

yes, the number reported is actually 10x what it should be: #6766

Copy link
Member

Choose a reason for hiding this comment

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

Unfortunately you'll have to rebase the test output after merging

}

// Filter equivalent cells based on the following liberty attributes:
// - Footprint (Optional - Honored if enforced by user): Cells with the
// same footprint have the same layout boundary.
// - User Function Class (Optional - Honored if found): Cells with the
// same user_function_class are electrically compatible.

bool Resizer::areCellsSwappable(LibertyCell* existing, LibertyCell* replacement)
Copy link
Member

Choose a reason for hiding this comment

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

@precisionmoon has added area and leakage optional limits to Resizer::getSwappableCells. Should they apply here too? If so it would be better to share the logic.

Copy link
Member

Choose a reason for hiding this comment

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

Actually I see you are sharing the code but the limits seem to have disappeared in the refactoring

static void populateBufferCapTestPoints(LibertyCell* cell,
LibertyPort* in,
LibertyPort* out,
std::vector<float>& points)
Copy link
Member

Choose a reason for hiding this comment

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

Any reason not to use a map here rather than a vector + sort + unique?

Copy link
Contributor

Choose a reason for hiding this comment

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

Well map is too much (we have nothing interesting to put in as values), but std::set<float> might be fine. At first I wasn't sure whether comparison of floats is robust enough, but Internet says it's OK to use as a key

Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
for (auto p : test_points) {
if ((wlimit == 0 || p <= wlimit) &&
/* ignore high C_load/C_in region */ p
< std::min(bin->capacitance(), win->capacitance()) * 20.0f) {
Copy link
Member

Choose a reason for hiding this comment

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

why 20?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's an arbitrary constant: we size cells for C_load/C_in <= 4 and buffers for C_load/C_in <= 9, and the value of 20 was chosen as something which is far enough from that. Some of these constants should probably be, at some point, replaced with a set_opt_config parameter

Copy link
Collaborator

Choose a reason for hiding this comment

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

Does it make sense to make this a constexpr variable so we can better document the purpose?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure, at this point it's clear there will be other changes from review so let me do that too

Signed-off-by: Martin Povišer <povik@cutebit.org>

bool Resizer::bufferSizeOutmatched(LibertyCell* worse,
LibertyCell* better,
float max_drive_resist)
Copy link
Member

Choose a reason for hiding this comment

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

const

&& !drvr->isConstant()) {
float fanout, max_fanout, fanout_slack;
sta_->checkFanout(drvr_pin, max_, fanout, max_fanout, fanout_slack);
max_fanout = 10.0f;
Copy link
Member

Choose a reason for hiding this comment

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

Why 10 and ignore the cell or design limit?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is a value which has worked well. Can we insert a flow default of max_fanout=10 or 20 after I remove this hard-coded value?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

There are many flows out there. We can add it to ORFS but if this really is the best thing they maybe it is better to keep it in OR. We can always add a flag if needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

I've removed the override. I think we can merge this and revisit later. We don't need to enable gain-based buffering in the flow right away

Signed-off-by: Martin Povišer <povik@cutebit.org>
@povik
Copy link
Contributor

povik commented Feb 28, 2025

@povik
Copy link
Contributor

povik commented Mar 4, 2025

After updates the QoR from this change has degraded: https://dashboard.precisioninno.com/compare?sourceAType=Commit&sourceBType=Commit&sourceBName=3a4a4b5c10a7f036120c4bae4be4e69c1c346062&sourceBID=21&sourceAName=a2e7a0fe4d14cb0382446a3843ee594f696f7de8

I'll need to investigate.

I've found the cause: getSwappableCells stopped returning the original size. I would've wished this came with more of a warning.

povik added 2 commits March 4, 2025 14:14
Signed-off-by: Martin Povišer <povik@cutebit.org>
Signed-off-by: Martin Povišer <povik@cutebit.org>
@@ -1418,10 +1590,6 @@ LibertyCellSeq Resizer::getSwappableCells(LibertyCell* source_cell)
source_cell_leakage = cellLeakage(source_cell);
}
for (LibertyCell* equiv_cell : *equiv_cells) {
if (equiv_cell == source_cell) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@precisionmoon I've put source_cell back into the swappable list. I think most users of the function expect it there.

Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't look like putting it back breaks anything. Let me know if there's something I need address.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, we want to move away from including the source cell in the list. Why go through N+1 vs. N? We want the clients to adopt to the new behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants