diff --git a/src/rsz/include/rsz/Resizer.hh b/src/rsz/include/rsz/Resizer.hh index 5cef9b927ab..8a162ad871b 100644 --- a/src/rsz/include/rsz/Resizer.hh +++ b/src/rsz/include/rsz/Resizer.hh @@ -479,6 +479,7 @@ class Resizer : public dbStaState bool hasMultipleOutputs(const Instance* inst); void resizePreamble(); + bool areCellsSwappable(LibertyCell* existing, LibertyCell* replacement); LibertyCellSeq getSwappableCells(LibertyCell* source_cell); // Resize drvr_pin instance to target slew. diff --git a/src/rsz/src/RepairDesign.cc b/src/rsz/src/RepairDesign.cc index 24487aa7edf..c53ad3ccb53 100644 --- a/src/rsz/src/RepairDesign.cc +++ b/src/rsz/src/RepairDesign.cc @@ -67,6 +67,9 @@ using sta::NetConnectedPinIterator; using sta::NetIterator; using sta::NetPinIterator; using sta::Port; +using sta::TimingArc; +using sta::TimingArcSet; +using sta::TimingRole; RepairDesign::RepairDesign(Resizer* resizer) : resizer_(resizer) { @@ -644,6 +647,110 @@ bool RepairDesign::performGainBuffering(Net* net, return repaired_net; } +void RepairDesign::checkDriverArcSlew(const Corner* corner, + const Instance* inst, + const TimingArc* arc, + float load_cap, + float limit, + float& violation) +{ + const DcalcAnalysisPt* dcalc_ap = corner->findDcalcAnalysisPt(max_); + RiseFall* in_rf = arc->fromEdge()->asRiseFall(); + GateTimingModel* model = dynamic_cast(arc->model()); + Pin* in_pin = network_->findPin(inst, arc->from()->name()); + + if (model && in_pin) { + Slew in_slew = sta_->graph()->slew( + graph_->pinLoadVertex(in_pin), in_rf, dcalc_ap->index()); + const Pvt* pvt = dcalc_ap->operatingConditions(); + + ArcDelay arc_delay; + Slew arc_slew; + model->gateDelay(pvt, in_slew, load_cap, false, arc_delay, arc_slew); + + if (arc_slew > limit) { + violation = max(arc_slew - limit, violation); + } + } +} + +// Repair max slew violation at a driver pin: Find the smallest +// size which fits max slew; if none can be found, at least pick +// the size for which the slew is lowest +bool RepairDesign::repairDriverSlew(const Corner* corner, const Pin* drvr_pin) +{ + Instance* inst = network_->instance(drvr_pin); + LibertyCell* cell = network_->libertyCell(inst); + + float load_cap; + resizer_->ensureWireParasitic(drvr_pin); + load_cap + = graph_delay_calc_->loadCap(drvr_pin, corner->findDcalcAnalysisPt(max_)); + + if (!network_->isTopLevelPort(drvr_pin) && !resizer_->dontTouch(inst) && cell + && resizer_->isLogicStdCell(inst)) { + LibertyCellSeq* equiv_cells = sta_->equivCells(cell); + if (equiv_cells) { + // Pair of slew violation magnitude and cell pointer + typedef std::pair SizeCandidate; + std::vector sizes; + + for (LibertyCell* size_cell : *equiv_cells) { + if (resizer_->areCellsSwappable(cell, size_cell)) { + float limit, violation = 0; + bool limit_exists = false; + LibertyPort* port + = size_cell->findLibertyPort(network_->portName(drvr_pin)); + sta_->findSlewLimit(port, corner, max_, limit, limit_exists); + + if (limit_exists) { + float limit_w_margin = maxSlewMargined(limit); + + for (TimingArcSet* arc_set : size_cell->timingArcSets()) { + TimingRole* role = arc_set->role(); + if (!role->isTimingCheck() + && role != TimingRole::tristateDisable() + && role != TimingRole::tristateEnable() + && role != TimingRole::clockTreePathMin() + && role != TimingRole::clockTreePathMax()) { + for (TimingArc* arc : arc_set->arcs()) { + if (arc->to() == port) { + checkDriverArcSlew( + corner, inst, arc, load_cap, limit_w_margin, violation); + } + } + } + } + } + + sizes.emplace_back(violation, size_cell); + } + } + + if (sizes.empty()) { + logger_->critical( + RSZ, 144, "sizes list empty for cell {}\n", cell->name()); + } + + std::sort( + sizes.begin(), sizes.end(), [](SizeCandidate a, SizeCandidate b) { + if (a.first == 0 && b.first == 0) { + // both sizes non-violating: sort by area + return a.second->area() < b.second->area(); + } + return a.first < b.first; + }); + + LibertyCell* selected_size = sizes.front().second; + if (selected_size != cell) { + return resizer_->replaceCell(inst, selected_size, true); + } + } + } + + return false; +} + void RepairDesign::repairNet(Net* net, const Pin* drvr_pin, Vertex* drvr, @@ -685,6 +792,7 @@ void RepairDesign::repairNet(Net* net, } } + // Fanout is addressed by creating region repeaters if (check_fanout) { float fanout, max_fanout, fanout_slack; sta_->checkFanout(drvr_pin, max_, fanout, max_fanout, fanout_slack); @@ -706,134 +814,112 @@ void RepairDesign::repairNet(Net* net, } } - // Resize the driver to normalize slews before repairing limit violations. + // TO BE REMOVED: Resize the driver to normalize slews before repairing + // limit violations. if (parasitics_src_ == ParasiticsSrc::placement && resize_drvr) { resize_count_ += resizer_->resizeToTargetSlew(drvr_pin); } + + float max_cap = INF; + bool repair_cap = false, repair_load_slew = false, repair_wire = false; + + resizer_->ensureWireParasitic(drvr_pin, net); + graph_delay_calc_->findDelays(drvr); + + if (check_slew) { + bool slew_violation = false; + + // First repair driver slew -- addressed by resizing the driver, + // and if that doesn't fix it fully, by inserting buffers + float slew1, max_slew1, slew_slack1; + const Corner* corner1; + checkSlew(drvr_pin, slew1, max_slew1, slew_slack1, corner1); + if (slew_slack1 < 0.0f) { + debugPrint(logger_, + RSZ, + "repair_net", + 2, + "drvr slew violation pin={} slew={} max_slew={}", + network_->name(drvr_pin), + delayAsString(slew1, this, 3), + delayAsString(max_slew1, this, 3)); + + slew_violation = true; + if (repairDriverSlew(corner1, drvr_pin)) { + resize_count_++; + checkSlew(drvr_pin, slew1, max_slew1, slew_slack1, corner1); + } + + // Slew violation persists after resizing the driver, derive + // the max cap we need to apply to remove the slew violation + if (slew_slack1 < 0.0f) { + LibertyPort* drvr_port = network_->libertyPort(drvr_pin); + if (drvr_port) { + max_cap = findSlewLoadCap(drvr_port, max_slew1, corner1); + corner = corner1; + repair_cap = true; + } + } + } + + if (!resizer_->isTristateDriver(drvr_pin)) { + // Check load slew, if violated it will be repaired by inserting + // buffers later + resizer_->checkLoadSlews( + drvr_pin, slew_margin_, slew1, max_slew1, slew_slack1, corner1); + if (slew_slack1 < 0.0f) { + debugPrint(logger_, + RSZ, + "repair_net", + 2, + "load slew violation pin={} load_slew={} max_slew={}", + network_->name(drvr_pin), + delayAsString(slew1, this, 3), + delayAsString(max_slew1, this, 3)); + + slew_violation = true; + repair_load_slew = true; + // If repair_cap is true, corner is already set to correspond + // to a max_cap violation, do not override in that case + if (!repair_cap) { + corner = corner1; + } + } + } + + if (slew_violation) { + slew_violations++; + } + } + + if (check_cap && !resizer_->isTristateDriver(drvr_pin)) { + if (needRepairCap(drvr_pin, cap_violations, max_cap, corner)) { + repair_cap = true; + } + } + // For tristate nets all we can do is resize the driver. if (!resizer_->isTristateDriver(drvr_pin)) { BufferedNetPtr bnet = resizer_->makeBufferedNetSteiner(drvr_pin, corner); if (bnet) { - resizer_->ensureWireParasitic(drvr_pin, net); - graph_delay_calc_->findDelays(drvr); - - float max_cap = INF; int wire_length = bnet->maxLoadWireLength(); - bool need_repair = needRepair(drvr_pin, - corner, - max_length, - wire_length, - check_cap, - check_slew, - max_cap, - slew_violations, - cap_violations, - length_violations); - - if (need_repair) { - if (parasitics_src_ == ParasiticsSrc::global_routing && resize_drvr) { - resize_count_ += resizer_->resizeToTargetSlew(drvr_pin); - wire_length = bnet->maxLoadWireLength(); - need_repair = needRepair(drvr_pin, - corner, - max_length, - wire_length, - check_cap, - check_slew, - max_cap, - slew_violations, - cap_violations, - length_violations); - } - if (need_repair) { - Point drvr_loc = db_network_->location(drvr->pin()); - debugPrint( - logger_, - RSZ, - "repair_net", - 1, - "driver {} ({} {}) l={}", - sdc_network_->pathName(drvr_pin), - units_->distanceUnit()->asString(dbuToMeters(drvr_loc.getX()), - 1), - units_->distanceUnit()->asString(dbuToMeters(drvr_loc.getY()), - 1), - units_->distanceUnit()->asString(dbuToMeters(wire_length), 1)); - repairNet(bnet, drvr_pin, max_cap, max_length, corner); - repaired_net = true; - - if (resize_drvr) { - resize_count_ += resizer_->resizeToTargetSlew(drvr_pin); - } - } + repair_wire + = needRepairWire(max_length, wire_length, length_violations); + + // Insert buffers on the Steiner tree if need be + if (repair_cap || repair_load_slew || repair_wire) { + repaired_net = true; + repairNet(bnet, drvr_pin, max_cap, max_length, corner); } } } + if (repaired_net) { repaired_net_count++; } } } -bool RepairDesign::needRepairSlew(const Pin* drvr_pin, - int& slew_violations, - float& max_cap, - const Corner*& corner) -{ - bool repair_slew = false; - float slew1, slew_slack1, max_slew1; - const Corner* corner1; - // Check slew at the driver. - checkSlew(drvr_pin, slew1, max_slew1, slew_slack1, corner1); - // Max slew violations at the driver pin are repaired by reducing the - // load capacitance. Wire resistance may shield capacitance from the - // driver but so this is conservative. - // Find max load cap that corresponds to max_slew. - LibertyPort* drvr_port = network_->libertyPort(drvr_pin); - if (corner1 && max_slew1 > 0.0) { - if (drvr_port) { - float max_cap1 = findSlewLoadCap(drvr_port, max_slew1, corner1); - max_cap = min(max_cap, max_cap1); - } - corner = corner1; - if (slew_slack1 < 0.0) { - debugPrint(logger_, - RSZ, - "repair_net", - 2, - "drvr slew violation slew={} max_slew={}", - delayAsString(slew1, this, 3), - delayAsString(max_slew1, this, 3)); - repair_slew = true; - slew_violations++; - } - } - // Check slew at the loads. - // Note that many liberty libraries do not have max_transition attributes on - // input pins. - // Max slew violations at the load pins are repaired by inserting buffers - // and reducing the wire length to the load. - resizer_->checkLoadSlews( - drvr_pin, slew_margin_, slew1, max_slew1, slew_slack1, corner1); - if (slew_slack1 < 0.0) { - debugPrint(logger_, - RSZ, - "repair_net", - 2, - "load slew violation load_slew={} max_slew={}", - delayAsString(slew1, this, 3), - delayAsString(max_slew1, this, 3)); - corner = corner1; - // Don't double count the driver/load on same net. - if (!repair_slew) { - slew_violations++; - } - repair_slew = true; - } - - return repair_slew; -} - bool RepairDesign::needRepairCap(const Pin* drvr_pin, int& cap_violations, float& max_cap, @@ -846,8 +932,9 @@ bool RepairDesign::needRepairCap(const Pin* drvr_pin, drvr_pin, nullptr, max_, corner1, tr1, cap1, max_cap1, cap_slack1); if (max_cap1 > 0.0 && corner1) { max_cap1 *= (1.0 - cap_margin_ / 100.0); - max_cap = max_cap1; + if (cap1 > max_cap1) { + max_cap = max_cap1; corner = corner1; cap_violations++; return true; @@ -868,69 +955,6 @@ bool RepairDesign::needRepairWire(const int max_length, return false; } -bool RepairDesign::needRepair(const Pin* drvr_pin, - const Corner*& corner, - const int max_length, - const int wire_length, - const bool check_cap, - const bool check_slew, - float& max_cap, - int& slew_violations, - int& cap_violations, - int& length_violations) -{ - bool repair_cap = false; - bool repair_slew = false; - if (check_cap) { - repair_cap = needRepairCap(drvr_pin, cap_violations, max_cap, corner); - } - bool repair_wire = needRepairWire(max_length, wire_length, length_violations); - if (check_slew) { - repair_slew = needRepairSlew(drvr_pin, slew_violations, max_cap, corner); - } - - return repair_cap || repair_wire || repair_slew; -} - -bool RepairDesign::checkLimits(const Pin* drvr_pin, - bool check_slew, - bool check_cap, - bool check_fanout) -{ - if (check_cap) { - float cap1, max_cap1, cap_slack1; - const Corner* corner1; - const RiseFall* tr1; - sta_->checkCapacitance( - drvr_pin, nullptr, max_, corner1, tr1, cap1, max_cap1, cap_slack1); - max_cap1 *= (1.0 - cap_margin_ / 100.0); - if (cap1 < max_cap1) { - return true; - } - } - if (check_fanout) { - float fanout, fanout_slack, max_fanout; - sta_->checkFanout(drvr_pin, max_, fanout, max_fanout, fanout_slack); - if (fanout_slack < 0.0) { - return true; - } - } - if (check_slew) { - float slew1, slew_slack1, max_slew1; - const Corner* corner1; - checkSlew(drvr_pin, slew1, max_slew1, slew_slack1, corner1); - if (slew_slack1 < 0.0) { - return true; - } - resizer_->checkLoadSlews( - drvr_pin, slew_margin_, slew1, max_slew1, slew_slack1, corner1); - if (slew_slack1 < 0.0) { - return true; - } - } - return false; -} - void RepairDesign::checkSlew(const Pin* drvr_pin, // Return values. Slew& slew, @@ -1652,8 +1676,8 @@ void RepairDesign::makeFanoutRepeater(PinSeq& repeater_loads, repeater_in_pin, repeater_out_pin); Vertex* repeater_out_vertex = graph_->pinDrvrVertex(repeater_out_pin); - int repaired_net_count, slew_violations, cap_violations = 0; - int fanout_violations, length_violations = 0; + int repaired_net_count = 0, slew_violations = 0, cap_violations = 0; + int fanout_violations = 0, length_violations = 0; repairNet(out_net, repeater_out_pin, repeater_out_vertex, diff --git a/src/rsz/src/RepairDesign.hh b/src/rsz/src/RepairDesign.hh index 3513b86caae..16bf6a12f42 100644 --- a/src/rsz/src/RepairDesign.hh +++ b/src/rsz/src/RepairDesign.hh @@ -115,6 +115,14 @@ class RepairDesign : dbStaState void findBufferSizes(); bool performGainBuffering(Net* net, const Pin* drvr_pin, int max_fanout); + void checkDriverArcSlew(const Corner* corner, + const Instance* inst, + const TimingArc* arc, + float load_cap, + float limit, + float& violation); + bool repairDriverSlew(const Corner* corner, const Pin* drvr_pin); + void repairNet(Net* net, const Pin* drvr_pin, Vertex* drvr, @@ -128,29 +136,11 @@ class RepairDesign : dbStaState int& cap_violations, int& fanout_violations, int& length_violations); - bool needRepairSlew(const Pin* drvr_pin, - int& slew_violations, - float& max_cap, - const Corner*& corner); bool needRepairCap(const Pin* drvr_pin, int& cap_violations, float& max_cap, const Corner*& corner); bool needRepairWire(int max_length, int wire_length, int& length_violations); - bool needRepair(const Pin* drvr_pin, - const Corner*& corner, - int max_length, - int wire_length, - bool check_cap, - bool check_slew, - float& max_cap, - int& slew_violations, - int& cap_violations, - int& length_violations); - bool checkLimits(const Pin* drvr_pin, - bool check_slew, - bool check_cap, - bool check_fanout); void checkSlew(const Pin* drvr_pin, // Return values. Slew& slew, diff --git a/src/rsz/src/Resizer.cc b/src/rsz/src/Resizer.cc index 91064aed56a..7a74925ad27 100644 --- a/src/rsz/src/Resizer.cc +++ b/src/rsz/src/Resizer.cc @@ -1168,6 +1168,22 @@ void Resizer::resizePreamble() // 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) +{ + if (isLinkCell(replacement) && !dontUse(replacement)) { + if (!match_cell_footprint_ + || sta::stringEqIf(existing->footprint(), replacement->footprint())) { + if (!existing->userFunctionClass() + || sta::stringEqIf(existing->userFunctionClass(), + replacement->userFunctionClass())) { + return true; + } + } + } + return false; +} + LibertyCellSeq Resizer::getSwappableCells(LibertyCell* source_cell) { dbMaster* master = db_network_->staToDb(source_cell); @@ -1180,24 +1196,12 @@ LibertyCellSeq Resizer::getSwappableCells(LibertyCell* source_cell) if (equiv_cells) { for (LibertyCell* equiv_cell : *equiv_cells) { - if (match_cell_footprint_) { - const bool footprints_match = sta::stringEqIf(source_cell->footprint(), - equiv_cell->footprint()); - if (!footprints_match) { - continue; - } - } - - if (source_cell->userFunctionClass()) { - const bool user_function_classes_match = sta::stringEqIf( - source_cell->userFunctionClass(), equiv_cell->userFunctionClass()); - if (!user_function_classes_match) { - continue; - } + if (areCellsSwappable(source_cell, equiv_cell)) { + swappable_cells.push_back(equiv_cell); } - - swappable_cells.push_back(equiv_cell); } + } else { + swappable_cells.push_back(source_cell); } return swappable_cells; diff --git a/src/rsz/test/repair_design2.ok b/src/rsz/test/repair_design2.ok index e967906a89d..f382409df1f 100644 --- a/src/rsz/test/repair_design2.ok +++ b/src/rsz/test/repair_design2.ok @@ -6,4 +6,5 @@ [INFO ODB-0132] Created 2 special nets and 0 connections. [INFO ODB-0133] Created 4 nets and 6 connections. [INFO RSZ-0058] Using max wire length 2406um. +[INFO RSZ-0034] Found 2 slew violations. [INFO RSZ-0039] Resized 3 instances. diff --git a/src/rsz/test/repair_design3.ok b/src/rsz/test/repair_design3.ok index 1f4bfe33765..571dccbe3df 100644 --- a/src/rsz/test/repair_design3.ok +++ b/src/rsz/test/repair_design3.ok @@ -6,4 +6,5 @@ [INFO ODB-0132] Created 2 special nets and 0 connections. [INFO ODB-0133] Created 2 nets and 1001 connections. [INFO RSZ-0058] Using max wire length 2406um. +[INFO RSZ-0034] Found 1000 slew violations. [INFO RSZ-0039] Resized 1000 instances. diff --git a/src/rsz/test/repair_design3_verbose.ok b/src/rsz/test/repair_design3_verbose.ok index b8ef3a2d110..0db4e1a686d 100644 --- a/src/rsz/test/repair_design3_verbose.ok +++ b/src/rsz/test/repair_design3_verbose.ok @@ -21,4 +21,5 @@ Iteration | Resized | Buffers | Nets repaired | Remaining 1000 | 998 | 0 | 0 | 2 final | 1000 | 0 | 0 | 0 --------------------------------------------------------- +[INFO RSZ-0034] Found 1000 slew violations. [INFO RSZ-0039] Resized 1000 instances. diff --git a/src/rsz/test/repair_slew12.ok b/src/rsz/test/repair_slew12.ok index 2d803420cd9..076266e7856 100644 --- a/src/rsz/test/repair_slew12.ok +++ b/src/rsz/test/repair_slew12.ok @@ -8,4 +8,4 @@ [INFO RSZ-0034] Found 1 slew violations. [INFO RSZ-0037] Found 1 long wires. [INFO RSZ-0038] Inserted 3 buffers in 2 nets. -[INFO RSZ-0039] Resized 2 instances. +[INFO RSZ-0039] Resized 3 instances. diff --git a/src/rsz/test/repair_slew16.ok b/src/rsz/test/repair_slew16.ok index 616e7f67340..3206081f412 100644 --- a/src/rsz/test/repair_slew16.ok +++ b/src/rsz/test/repair_slew16.ok @@ -26,7 +26,7 @@ Pin Limit Cap Slack ------------------------------------------------------------ drvr/Q 60.73 31.21 29.52 (MET) -[INFO RSZ-0034] Found 2 slew violations. +[INFO RSZ-0034] Found 1 slew violations. [INFO RSZ-0039] Resized 1 instances. max slew diff --git a/src/rsz/test/repair_wire10.ok b/src/rsz/test/repair_wire10.ok index 58b288c39b5..cddba6392ff 100644 --- a/src/rsz/test/repair_wire10.ok +++ b/src/rsz/test/repair_wire10.ok @@ -39,9 +39,9 @@ Driver length delay u2/Z manhtn 1998.2 steiner 1998.2 0.00 u3/Z manhtn 1.3 steiner 1.3 0.00 in1 manhtn 0.7 steiner 0.7 0.00 -[INFO RSZ-0034] Found 2 slew violations. +[INFO RSZ-0034] Found 1 slew violations. [INFO RSZ-0036] Found 1 capacitance violations. -[INFO RSZ-0037] Found 2 long wires. +[INFO RSZ-0037] Found 1 long wires. [INFO RSZ-0038] Inserted 2 buffers in 1 nets. [INFO RSZ-0039] Resized 1 instances. Startpoint: in1 (input port) @@ -54,22 +54,22 @@ Path Type: max 0.00 0.00 v input external delay 1.16 0.00 0.00 0.00 v in1 (in) 0.00 0.00 0.00 v u1/A (BUF_X1) - 11.19 0.01 0.04 0.04 v u1/Z (BUF_X1) - 0.01 0.00 0.04 v u2/A (BUF_X16) - 54.83 0.01 0.03 0.07 v u2/Z (BUF_X16) - 0.06 0.05 0.12 v wire2/A (BUF_X8) - 50.19 0.01 0.05 0.17 v wire2/Z (BUF_X8) - 0.05 0.04 0.21 v wire1/A (BUF_X8) - 58.05 0.01 0.05 0.26 v wire1/Z (BUF_X8) - 0.07 0.06 0.32 v u3/A (BUF_X1) - 0.24 0.01 0.05 0.36 v u3/Z (BUF_X1) - 0.01 0.00 0.36 v out1 (out) - 0.36 data arrival time + 1.79 0.01 0.02 0.02 v u1/Z (BUF_X1) + 0.01 0.00 0.02 v u2/A (BUF_X2) + 54.93 0.02 0.04 0.07 v u2/Z (BUF_X2) + 0.07 0.06 0.12 v wire2/A (BUF_X8) + 50.19 0.01 0.05 0.18 v wire2/Z (BUF_X8) + 0.05 0.04 0.22 v wire1/A (BUF_X8) + 58.05 0.01 0.05 0.27 v wire1/Z (BUF_X8) + 0.07 0.06 0.33 v u3/A (BUF_X1) + 0.24 0.01 0.05 0.37 v u3/Z (BUF_X1) + 0.01 0.00 0.37 v out1 (out) + 0.37 data arrival time ----------------------------------------------------------------------- (Path is unconstrained) Driver length delay wire1/Z manhtn 758.4 steiner 758.4 0.00 -u2/Z manhtn 647.6 steiner 647.6 0.00 +u2/Z manhtn 650.2 steiner 650.2 0.00 wire2/Z manhtn 586.6 steiner 586.6 0.00