Skip to content

Commit 35398eb

Browse files
rsz: try to upsize driver when repairing load slew
Buffers created by repairNetWire now have a name dependant on their reason, similar to repairNetJunc Signed-off-by: Tobias Senti <git@tsenti.li>
1 parent a00a575 commit 35398eb

27 files changed

Lines changed: 457 additions & 229 deletions

src/rsz/include/rsz/Resizer.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,7 @@ class Resizer : public sta::dbStaState, public sta::dbNetworkObserver
613613
bool hasMultipleOutputs(const sta::Instance* inst);
614614

615615
void resizePreamble();
616+
void clearSwappableCellsCache();
616617
sta::LibertyCellSeq getSwappableCells(sta::LibertyCell* source_cell);
617618
sta::LibertyCellSeq getVTEquivCells(sta::LibertyCell* source_cell);
618619

src/rsz/src/RepairDesign.cc

Lines changed: 231 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
16591843
void 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) {

src/rsz/src/RepairDesign.hh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,14 @@ class RepairDesign : sta::dbStaState
174174
// Return values.
175175
int& wire_length,
176176
sta::PinSeq& load_pins);
177+
void clearDriverCellsCache();
178+
sta::LibertyCellSeq getDriverCells(sta::LibertyCell* driver_cell,
179+
std::string driver_port_name);
180+
bool tryUpsizeDriver(int level,
181+
double ref_cap,
182+
double c_wire,
183+
double r_wire,
184+
float max_load_slew_margined);
177185
float maxSlewMargined(float max_slew);
178186
double findSlewLoadCap(sta::LibertyPort* drvr_port,
179187
double slew,
@@ -256,8 +264,14 @@ class RepairDesign : sta::dbStaState
256264
// Gain buffering
257265
std::vector<sta::LibertyCell*> buffer_sizes_;
258266

267+
// Swappable driver cells sorted by drive resistance (low to high)
268+
std::unordered_map<sta::LibertyCell*,
269+
std::unordered_map<std::string, sta::LibertyCellSeq>>
270+
driver_cells_cache_;
271+
259272
// Implicit arguments to repairNet bnet recursion.
260273
const sta::Pin* drvr_pin_ = nullptr;
274+
bool drvr_resized_ = false;
261275
float max_cap_ = 0;
262276
int max_length_ = 0;
263277
double slew_margin_ = 0;

0 commit comments

Comments
 (0)