@@ -1075,6 +1075,8 @@ void RepairDesign::repairNet(sta::Net* net,
10751075 estimate_parasitics_->ensureWireParasitic (drvr_pin, net);
10761076 graph_delay_calc_->findDelays (drvr);
10771077
1078+ drvr_resized_ = false ;
1079+
10781080 if (check_slew) {
10791081 bool slew_violation = false ;
10801082
@@ -1096,7 +1098,7 @@ void RepairDesign::repairNet(sta::Net* net,
10961098
10971099 slew_violation = true ;
10981100 if (repairDriverSlew (corner1, drvr_pin)) {
1099- resize_count_++ ;
1101+ drvr_resized_ = true ;
11001102 estimate_parasitics_->updateParasitics ();
11011103 sta_->findDelays (drvr);
11021104 checkSlew (drvr_pin, slew1, max_slew1, slew_slack1, corner1);
@@ -1181,6 +1183,10 @@ void RepairDesign::repairNet(sta::Net* net,
11811183 }
11821184 }
11831185
1186+ if (drvr_resized_) {
1187+ resize_count_++;
1188+ }
1189+
11841190 if (repaired_net) {
11851191 repaired_net_count++;
11861192 }
@@ -1499,6 +1505,7 @@ void RepairDesign::repairNetWire(
14991505 // offset from instance origin to pin and detailed placement movement.
15001506 constexpr double length_margin = .05 ;
15011507 bool split_wire = false ;
1508+ const char * split_reason = " " ;
15021509 // Distance from repeater to ref_.
15031510 // length
15041511 // from----------------------------to/ref
@@ -1518,6 +1525,7 @@ void RepairDesign::repairNetWire(
15181525 units_->distanceUnit ()->asString (dbuToMeters (max_length_), 1 ));
15191526 split_length = min (max (max_length_ - wire_length_ref, 0 ), length / 2 );
15201527 split_wire = true ;
1528+ split_reason = " wire_length" ;
15211529 }
15221530 if (wire_cap > 0.0 && load_cap > max_cap_) {
15231531 debugPrint (logger_,
@@ -1532,6 +1540,7 @@ void RepairDesign::repairNetWire(
15321540 split_length = min (split_length,
15331541 max (metersToDbu ((max_cap_ - ref_cap) / wire_cap), 0 ));
15341542 split_wire = true ;
1543+ split_reason = " wire_cap" ;
15351544 }
15361545 if (load_slew > max_load_slew_margined) {
15371546 debugPrint (logger_,
@@ -1543,44 +1552,53 @@ void RepairDesign::repairNetWire(
15431552 level,
15441553 delayAsString (load_slew, 3 , this ),
15451554 delayAsString (max_load_slew_margined, 3 , this ));
1546- // We are inserting a buffer to cut this wire segment short.
1547- // The slew at the end of the wire segment is a quadratic polynomial
1548- // in terms of the wire segment's length (in Elmore approx.).
1549- //
1550- // We solve a quadratic eq. to find the maximum conforming length.
1551- float a = wire_res * wire_cap / 2 ;
1552- float b = (r_drvr * wire_cap) + (wire_res * ref_cap);
1553- float c
1554- = (r_drvr * ref_cap) - (max_load_slew_margined / (*slew_rc_factor_));
1555- float l = 0.0 ;
1556- if (a > 1e-12 ) { // Quadratic case
1557- const float discriminant = b * b - 4 * a * c;
1558- if (discriminant >= 0.0 ) {
1559- l = (-b + sqrt (discriminant)) / (2 * a);
1555+
1556+ // Check if we can upsize the driver to meet the slew requirement without
1557+ // inserting a buffer. Don't bother if we anyways insert a buffer for
1558+ // length or capacitance reasons.
1559+ if (split_wire
1560+ || !tryUpsizeDriver (
1561+ level, ref_cap, c_wire, r_wire, max_load_slew_margined)) {
1562+ // We are inserting a buffer to cut this wire segment short.
1563+ // The slew at the end of the wire segment is a quadratic polynomial
1564+ // in terms of the wire segment's length (in Elmore approx.).
1565+ //
1566+ // We solve a quadratic eq. to find the maximum conforming length.
1567+ float a = wire_res * wire_cap / 2 ;
1568+ float b = (r_drvr * wire_cap) + (wire_res * ref_cap);
1569+ float c = (r_drvr * ref_cap)
1570+ - (max_load_slew_margined / (*slew_rc_factor_));
1571+ float l = 0.0 ;
1572+ if (a > 1e-12 ) { // Quadratic case
1573+ const float discriminant = b * b - 4 * a * c;
1574+ if (discriminant >= 0.0 ) {
1575+ l = (-b + sqrt (discriminant)) / (2 * a);
1576+ }
1577+ } else if (b > 1e-12 ) {
1578+ // a * l^2 + b * l + c = 0 becomes
1579+ // b * l + c = 0 when a is very small
1580+ l = -c / b;
15601581 }
1561- } else if (b > 1e-12 ) {
1562- // a * l^2 + b * l + c = 0 becomes
1563- // b * l + c = 0 when a is very small
1564- l = -c / b;
1565- }
1566- if (l >= 0.0 ) {
1567- split_length = min (split_length, metersToDbu (l));
1568- } else {
1569- split_length = 0 ;
1582+ if (l >= 0.0 ) {
1583+ split_length = min (split_length, metersToDbu (l));
1584+ } else {
1585+ split_length = 0 ;
1586+ }
1587+ split_wire = true ;
1588+ split_reason = " wire_slew" ;
15701589 }
1571- split_wire = true ;
15721590 }
15731591
15741592 if (split_wire) {
1575- debugPrint (
1576- logger_ ,
1577- RSZ ,
1578- " repair_net " ,
1579- 3 ,
1580- " {:{}s}split length={} " ,
1581- " " ,
1582- level ,
1583- units_-> distanceUnit ()-> asString ( dbuToMeters (split_length), 1 ) );
1593+ debugPrint (logger_,
1594+ RSZ ,
1595+ " repair_net " ,
1596+ 3 ,
1597+ " {:{}s}split length={} reason={} " ,
1598+ " " ,
1599+ level ,
1600+ units_-> distanceUnit ()-> asString ( dbuToMeters (split_length), 1 ) ,
1601+ split_reason );
15841602 // Distance from to_pt to repeater backward toward from_pt.
15851603 // Note that split_length can be longer than the wire length
15861604 // because it is the maximum value that satisfies max slew/cap.
@@ -1593,7 +1611,7 @@ void RepairDesign::repairNetWire(
15931611 int buf_x = to_x + d * dx;
15941612 int buf_y = to_y + d * dy;
15951613 float repeater_cap, repeater_fanout;
1596- if (!makeRepeater (" wire " ,
1614+ if (!makeRepeater (split_reason ,
15971615 odb::Point (buf_x, buf_y),
15981616 buffer_cell,
15991617 /* resize= */ true ,
@@ -1656,6 +1674,172 @@ float RepairDesign::maxSlewMargined(float max_slew)
16561674 return max_slew * (1.0 - slew_margin_ / 100.0 );
16571675}
16581676
1677+ void RepairDesign::clearDriverCellsCache ()
1678+ {
1679+ driver_cells_cache_.clear ();
1680+ }
1681+
1682+ // Returns swappable drivers sorted by drive resistance (low to high)
1683+ sta::LibertyCellSeq RepairDesign::getDriverCells (sta::LibertyCell* driver_cell,
1684+ std::string driver_port_name)
1685+ {
1686+ if (driver_cells_cache_.find (driver_cell) != driver_cells_cache_.end ()) {
1687+ if (driver_cells_cache_[driver_cell].find (driver_port_name)
1688+ != driver_cells_cache_[driver_cell].end ()) {
1689+ return driver_cells_cache_[driver_cell][driver_port_name];
1690+ }
1691+ } else {
1692+ driver_cells_cache_[driver_cell] = {};
1693+ }
1694+
1695+ sta::LibertyCellSeq swappable_cells
1696+ = resizer_->getSwappableCells (driver_cell);
1697+
1698+ std::ranges::sort (
1699+ swappable_cells,
1700+ [driver_port_name](sta::LibertyCell* a, sta::LibertyCell* b) {
1701+ sta::LibertyPort* port_a = a->findLibertyPort (driver_port_name);
1702+ sta::LibertyPort* port_b = b->findLibertyPort (driver_port_name);
1703+ return port_a->driveResistance () < port_b->driveResistance ();
1704+ });
1705+
1706+ driver_cells_cache_[driver_cell][driver_port_name] = swappable_cells;
1707+ return swappable_cells;
1708+ }
1709+
1710+ bool RepairDesign::tryUpsizeDriver (const int level,
1711+ const double ref_cap,
1712+ const double c_wire,
1713+ const double r_wire,
1714+ const float max_load_slew_margined)
1715+ {
1716+ if (network_->isTopLevelPort (drvr_pin_)) {
1717+ return false ;
1718+ }
1719+
1720+ sta::Instance* driver_inst = network_->instance (drvr_pin_);
1721+ if (resizer_->dontTouch (driver_inst)) {
1722+ return false ;
1723+ }
1724+
1725+ debugPrint (
1726+ logger_,
1727+ RSZ ,
1728+ " repair_net" ,
1729+ 3 ,
1730+ " {:{}s}considering resizing driver {} to meet slew/cap requirement "
1731+ " without buffering" ,
1732+ " " ,
1733+ level,
1734+ network_->pathName (drvr_pin_));
1735+
1736+ sta::LibertyCell* driver_cell = network_->libertyCell (driver_inst);
1737+ std::string driver_port_name = network_->portName (drvr_pin_);
1738+ sta::LibertyCellSeq swappable_cells
1739+ = getDriverCells (driver_cell, driver_port_name);
1740+
1741+ const float orig_r_drvr = resizer_->driveResistance (drvr_pin_);
1742+
1743+ if (!swappable_cells.empty ()) {
1744+ sta::LibertyCell* upsized_driver_cell = nullptr ;
1745+ for (sta::LibertyCell* candidate_cell : swappable_cells) {
1746+ sta::LibertyPort* candidate_port
1747+ = candidate_cell->findLibertyPort (driver_port_name);
1748+
1749+ const float candidate_r_drvr = candidate_port->driveResistance ();
1750+
1751+ if (candidate_r_drvr >= orig_r_drvr) {
1752+ debugPrint (
1753+ logger_,
1754+ RSZ ,
1755+ " repair_net" ,
1756+ 3 ,
1757+ " {:{}s}candidate cell {} has drive resistance {} which is not "
1758+ " lower than current driver resistance {}, stopping search" ,
1759+ " " ,
1760+ level,
1761+ candidate_cell->name (),
1762+ units_->resistanceUnit ()->asString (candidate_r_drvr, 3 ),
1763+ units_->resistanceUnit ()->asString (orig_r_drvr, 3 ));
1764+ break ;
1765+ }
1766+
1767+ debugPrint (logger_,
1768+ RSZ ,
1769+ " repair_net" ,
1770+ 3 ,
1771+ " {:{}s}considering candidate cell {} with drive resistance {}" ,
1772+ " " ,
1773+ level,
1774+ candidate_cell->name (),
1775+ units_->resistanceUnit ()->asString (candidate_r_drvr, 3 ));
1776+
1777+ assert (slew_rc_factor_.has_value ());
1778+ const double candidate_load_slew
1779+ = (candidate_r_drvr * (c_wire + ref_cap) + r_wire * ref_cap
1780+ + r_wire * c_wire / 2 )
1781+ * (*slew_rc_factor_);
1782+
1783+ // Check that we do not violate the max output slew of the driver.
1784+ float candidate_max_output_slew = sta::INF ;
1785+ {
1786+ float max_output_slew = sta::INF ;
1787+ bool exists;
1788+ candidate_port->slewLimit (max_, max_output_slew, exists);
1789+ if (exists) {
1790+ candidate_max_output_slew = maxSlewMargined (max_output_slew);
1791+ }
1792+ }
1793+
1794+ if (candidate_load_slew > max_load_slew_margined) {
1795+ // Break as remaining candidates all have higher drive resistance
1796+ break ;
1797+ }
1798+
1799+ if (candidate_load_slew <= candidate_max_output_slew) {
1800+ debugPrint (
1801+ logger_,
1802+ RSZ ,
1803+ " repair_net" ,
1804+ 3 ,
1805+ " {:{}s}resizing driver to {} meets slew requirement with estimated "
1806+ " load slew {} <= {} and max output slew {} <= {}" ,
1807+ " " ,
1808+ level,
1809+ candidate_cell->name (),
1810+ delayAsString (candidate_load_slew, 3 , this ),
1811+ delayAsString (max_load_slew_margined, 3 , this ),
1812+ delayAsString (candidate_load_slew, 3 , this ),
1813+ delayAsString (candidate_max_output_slew, 3 , this ));
1814+ upsized_driver_cell = candidate_cell;
1815+ }
1816+ }
1817+
1818+ if (upsized_driver_cell) {
1819+ // We found a candidate cell that can be swapped to that meets the
1820+ // slew requirement
1821+ debugPrint (logger_,
1822+ RSZ ,
1823+ " repair_net" ,
1824+ 3 ,
1825+ " {:{}s}resizing driver from {} to {} to meet slew requirement "
1826+ " without buffering" ,
1827+ " " ,
1828+ level,
1829+ driver_cell->name (),
1830+ upsized_driver_cell->name ());
1831+ resizer_->replaceCell (driver_inst, upsized_driver_cell, true );
1832+
1833+ // Update drvr_pin_ to point to the new cell's output pin
1834+ drvr_pin_ = network_->findPin (driver_inst, driver_port_name);
1835+ drvr_resized_ = true ;
1836+ return true ;
1837+ }
1838+ }
1839+
1840+ return false ;
1841+ }
1842+
16591843void RepairDesign::repairNetJunc (
16601844 const BufferedNetPtr& bnet,
16611845 int level,
@@ -1756,15 +1940,20 @@ void RepairDesign::repairNetJunc(
17561940 level,
17571941 delayAsString (slew_slack_left, 3 , this ),
17581942 delayAsString (slew_slack_right, 3 , this ));
1759- // Isolate the branch with the smaller slack.
1760- if (slew_slack_left < slew_slack_right) {
1761- repeater_left = true ;
1762- } else {
1763- repeater_right = true ;
1943+
1944+ // Check if we can upsize the driver to meet the slew requirement without
1945+ // inserting a buffer.
1946+ if (!tryUpsizeDriver (level, load_cap, 0.0 , 0.0 , max_load_slew_margined)) {
1947+ // Isolate the branch with the smaller slack.
1948+ if (slew_slack_left < slew_slack_right) {
1949+ repeater_left = true ;
1950+ } else {
1951+ repeater_right = true ;
1952+ }
1953+ repeater_reason = " load_slew" ;
17641954 }
1765- repeater_reason = " load_slew" ;
17661955 }
1767- bool cap_violation = (cap_left + cap_right) > max_cap_;
1956+ bool cap_violation = load_cap > max_cap_;
17681957 if (cap_violation) {
17691958 debugPrint (logger_, RSZ , " repair_net" , 3 , " {:{}s}cap violation" , " " , level);
17701959 if (cap_left > cap_right) {
0 commit comments