Skip to content

Commit df2aeb7

Browse files
committed
rsz: Repair borrowed latch fanin paths
Extend setup repair target collection to include the latch D fanin path stored on latch-through timing paths. Rank latch D fanin targets with the downstream path targets so repair_timing can optimize logic that consumed borrowed time instead of only optimizing after the latch Q pin. Update inferred clock gator regression goldens and add C++ coverage for latch D fanin target collection. Test: ctest --test-dir build -R 'TestResizer\.(UncoveredBorrowReducesEffectiveSlack|CoveredBorrowKeepsReportedSlack|LatchThroughPathCollectsLatchDataFaninTargets)' --output-on-failure Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
1 parent d205112 commit df2aeb7

7 files changed

Lines changed: 209 additions & 30 deletions

src/rsz/src/RepairTargetCollector.cc

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,57 @@ sta::Path* RepairTargetCollector::findWorstSlackPath(
10181018
return sta_->vertexWorstSlackPath(endpoint, max_);
10191019
}
10201020

1021+
const sta::Path* RepairTargetCollector::latchDataPath(
1022+
const sta::PathExpanded& expanded) const
1023+
{
1024+
const sta::Path* d_path = nullptr;
1025+
const sta::Path* q_path = nullptr;
1026+
sta::Edge* d_q_edge = nullptr;
1027+
expanded.latchPaths(d_path, q_path, d_q_edge);
1028+
return d_path;
1029+
}
1030+
1031+
void RepairTargetCollector::collectExpandedPathDriverTargets(
1032+
const sta::Path* endpoint_path,
1033+
const sta::PathExpanded& expanded,
1034+
const sta::Slack path_slack,
1035+
std::vector<Target>& targets) const
1036+
{
1037+
const int start_index = static_cast<int>(expanded.startIndex());
1038+
const int path_count = static_cast<int>(expanded.size());
1039+
for (int index = start_index; index < path_count; ++index) {
1040+
const sta::Path* driver_path = expanded.path(index);
1041+
sta::Vertex* vertex = driver_path->vertex(sta_);
1042+
sta::Pin* pin = driver_path->pin(sta_);
1043+
if (vertex == nullptr || pin == nullptr || !vertex->isDriver(network_)
1044+
|| network_->isTopLevelPort(pin)) {
1045+
continue;
1046+
}
1047+
1048+
targets.push_back(makePathDriverTarget(
1049+
endpoint_path, expanded, index, path_slack, *resizer_));
1050+
}
1051+
}
1052+
1053+
void RepairTargetCollector::collectExpandedPathDriverPins(
1054+
const sta::PathExpanded& expanded,
1055+
set<const sta::Pin*>& pins) const
1056+
{
1057+
const int start_index = static_cast<int>(expanded.startIndex());
1058+
const int path_count = static_cast<int>(expanded.size());
1059+
for (int index = start_index; index < path_count; ++index) {
1060+
const sta::Path* driver_path = expanded.path(index);
1061+
sta::Vertex* vertex = driver_path->vertex(sta_);
1062+
const sta::Pin* pin = driver_path->pin(sta_);
1063+
if (vertex == nullptr || pin == nullptr || !vertex->isDriver(network_)
1064+
|| network_->isTopLevelPort(pin)
1065+
|| sta_->isClock(pin, sta_->cmdMode())) {
1066+
continue;
1067+
}
1068+
pins.insert(pin);
1069+
}
1070+
}
1071+
10211072
std::vector<Target> RepairTargetCollector::collectPathDriverTargets(
10221073
sta::Path* path,
10231074
const sta::Slack path_slack) const
@@ -1029,17 +1080,15 @@ std::vector<Target> RepairTargetCollector::collectPathDriverTargets(
10291080

10301081
sta::PathExpanded expanded(path, sta_);
10311082
targets.reserve(expanded.size());
1032-
for (int index = expanded.startIndex(); index < expanded.size(); ++index) {
1033-
const sta::Path* driver_path = expanded.path(index);
1034-
sta::Vertex* vertex = driver_path->vertex(sta_);
1035-
sta::Pin* pin = driver_path->pin(sta_);
1036-
if (vertex == nullptr || pin == nullptr || !vertex->isDriver(network_)
1037-
|| network_->isTopLevelPort(pin)) {
1038-
continue;
1039-
}
1083+
collectExpandedPathDriverTargets(path, expanded, path_slack, targets);
10401084

1041-
targets.push_back(
1042-
makePathDriverTarget(path, expanded, index, path_slack, *resizer_));
1085+
// A latch-through path starts at latch Q in OpenSTA, with the latch D path
1086+
// stored as side context. Expand that D path separately so repair_timing can
1087+
// optimize the logic that consumed the borrowed time.
1088+
const sta::Path* d_path = latchDataPath(expanded);
1089+
if (d_path != nullptr) {
1090+
sta::PathExpanded d_expanded(d_path, sta_);
1091+
collectExpandedPathDriverTargets(d_path, d_expanded, path_slack, targets);
10431092
}
10441093

10451094
return targets;
@@ -1359,6 +1408,19 @@ set<const sta::Pin*> RepairTargetCollector::collectPinsByPathEndpoint(
13591408
}
13601409
}
13611410
}
1411+
1412+
const sta::Path* d_path = latchDataPath(expanded);
1413+
if (d_path != nullptr) {
1414+
sta::PathExpanded d_expanded(d_path, sta_);
1415+
const size_t old_size = viol_pins.size();
1416+
collectExpandedPathDriverPins(d_expanded, viol_pins);
1417+
debugPrint(logger_,
1418+
RSZ,
1419+
"violator_collector",
1420+
4,
1421+
"Added {} latch D fanin pins from latch-through path",
1422+
viol_pins.size() - old_size);
1423+
}
13621424
debugPrint(logger_, RSZ, "violator_collector", 5, "\n");
13631425
}
13641426

src/rsz/src/RepairTargetCollector.hh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
#include "sta/Sta.hh"
2424
#include "utl/Logger.h"
2525

26+
namespace sta {
27+
class PathExpanded;
28+
}
29+
2630
namespace rsz {
2731

2832
using std::pair;
@@ -290,6 +294,13 @@ class RepairTargetCollector
290294
void sortByHeuristic(float load_delay_threshold = 0.0);
291295
std::map<const sta::Pin*, sta::Delay> getLocalTns() const;
292296
sta::Delay getLocalPinTns(const sta::Pin* pin) const;
297+
const sta::Path* latchDataPath(const sta::PathExpanded& expanded) const;
298+
void collectExpandedPathDriverTargets(const sta::Path* endpoint_path,
299+
const sta::PathExpanded& expanded,
300+
sta::Slack path_slack,
301+
std::vector<Target>& targets) const;
302+
void collectExpandedPathDriverPins(const sta::PathExpanded& expanded,
303+
set<const sta::Pin*>& pins) const;
293304
std::vector<const sta::Pin*> latchOutputPins(sta::Vertex* endpoint) const;
294305
sta::Slack latchOutputWorstSlack(
295306
const std::vector<const sta::Pin*>& output_pins) const;

src/rsz/src/policy/SetupLegacyBase.cc

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ using std::vector;
5353

5454
using utl::RSZ;
5555

56+
namespace {
57+
58+
const sta::Path* latchDataPath(const sta::PathExpanded& expanded)
59+
{
60+
const sta::Path* d_path = nullptr;
61+
const sta::Path* q_path = nullptr;
62+
sta::Edge* d_q_edge = nullptr;
63+
expanded.latchPaths(d_path, q_path, d_q_edge);
64+
return d_path;
65+
}
66+
67+
} // namespace
68+
5669
SetupLegacyBase::SetupLegacyBase(Resizer& resizer,
5770
MoveCommitter& committer,
5871
RepairSetupContext& setup_context,
@@ -455,7 +468,34 @@ bool SetupLegacyBase::makePinTargetOnPath(const sta::Pin* pin,
455468
}
456469

457470
sta::PathExpanded expanded(path, sta_);
458-
for (int index = expanded.startIndex(); index < expanded.size(); index++) {
471+
if (makePinTargetInExpandedPath(pin, path, expanded, focus_slack, target)) {
472+
return true;
473+
}
474+
475+
const sta::Path* d_path = latchDataPath(expanded);
476+
if (d_path == nullptr) {
477+
return false;
478+
}
479+
480+
sta::PathExpanded d_expanded(d_path, sta_);
481+
return makePinTargetInExpandedPath(
482+
pin, d_path, d_expanded, focus_slack, target);
483+
}
484+
485+
bool SetupLegacyBase::makePinTargetInExpandedPath(const sta::Pin* pin,
486+
const sta::Path* path,
487+
sta::PathExpanded& expanded,
488+
const sta::Slack focus_slack,
489+
Target& target) const
490+
{
491+
sta::Vertex* vertex = graph_->pinDrvrVertex(pin);
492+
if (vertex == nullptr) {
493+
return false;
494+
}
495+
496+
const int start_index = static_cast<int>(expanded.startIndex());
497+
const int path_count = static_cast<int>(expanded.size());
498+
for (int index = start_index; index < path_count; index++) {
459499
const sta::Path* driver_path = expanded.path(index);
460500
sta::Vertex* path_vertex = driver_path->vertex(sta_);
461501
const sta::Pin* path_pin = driver_path->pin(sta_);
@@ -737,7 +777,7 @@ bool SetupLegacyBase::repairPath(sta::Path* path,
737777
return false;
738778
}
739779

740-
const auto load_delays
780+
const std::vector<std::pair<int, sta::Delay>> load_delays
741781
= rankPathDrivers(expanded, corner, corner->libertyIndex(resizer_.max_));
742782
const int repairs_per_pass = repairBudget(path_slack, force_single_repair);
743783

@@ -751,13 +791,40 @@ bool SetupLegacyBase::repairPath(sta::Path* path,
751791
load_delays.size());
752792

753793
// Construct target vector
754-
std::vector<Target> targets;
755-
targets.reserve(load_delays.size());
756-
for (const auto& [drvr_index, ignored] : load_delays) {
757-
static_cast<void>(ignored);
794+
std::vector<std::pair<Target, sta::Delay>> ranked_targets;
795+
ranked_targets.reserve(load_delays.size());
796+
for (const std::pair<int, sta::Delay>& load_delay : load_delays) {
758797
Target target;
759-
makePathDriverTarget(path, expanded, drvr_index, path_slack, target);
760-
targets.push_back(std::move(target));
798+
makePathDriverTarget(path, expanded, load_delay.first, path_slack, target);
799+
ranked_targets.emplace_back(std::move(target), load_delay.second);
800+
}
801+
802+
const sta::Path* d_path = latchDataPath(expanded);
803+
if (d_path != nullptr) {
804+
sta::PathExpanded d_expanded(d_path, sta_);
805+
const sta::Scene* d_corner = d_path->scene(sta_);
806+
const std::vector<std::pair<int, sta::Delay>> d_load_delays
807+
= rankPathDrivers(
808+
d_expanded, d_corner, d_corner->libertyIndex(resizer_.max_));
809+
ranked_targets.reserve(ranked_targets.size() + d_load_delays.size());
810+
for (const std::pair<int, sta::Delay>& load_delay : d_load_delays) {
811+
Target target;
812+
makePathDriverTarget(
813+
d_path, d_expanded, load_delay.first, path_slack, target);
814+
ranked_targets.emplace_back(std::move(target), load_delay.second);
815+
}
816+
}
817+
818+
std::ranges::stable_sort(ranked_targets,
819+
[](const std::pair<Target, sta::Delay>& lhs,
820+
const std::pair<Target, sta::Delay>& rhs) {
821+
return lhs.second > rhs.second;
822+
});
823+
824+
std::vector<Target> targets;
825+
targets.reserve(ranked_targets.size());
826+
for (std::pair<Target, sta::Delay>& ranked_target : ranked_targets) {
827+
targets.push_back(std::move(ranked_target.first));
761828
}
762829

763830
// Prewarm for legacy MT policy

src/rsz/src/policy/SetupLegacyBase.hh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ class SetupLegacyBase : public OptimizationPolicy
140140
const sta::Path* path,
141141
sta::Slack focus_slack,
142142
Target& target) const;
143+
bool makePinTargetInExpandedPath(const sta::Pin* pin,
144+
const sta::Path* path,
145+
sta::PathExpanded& expanded,
146+
sta::Slack focus_slack,
147+
Target& target) const;
143148
bool makePinTarget(const sta::Pin* pin,
144149
sta::Slack focus_slack,
145150
Target& target) const;

src/rsz/test/cpp/TestResizer.cc

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,17 +157,17 @@ class TestResizer : public tst::IntegratedFixture
157157
sta_->updateTiming(true);
158158
}
159159

160-
sta::Vertex* latchDataEndpoint() const
160+
sta::Vertex* loadVertex(const char* pin_name) const
161161
{
162-
sta::Pin* latch_d = db_network_->findPin("enable_latch/D");
163-
if (latch_d == nullptr) {
164-
ADD_FAILURE() << "missing latch D pin";
162+
sta::Pin* pin = db_network_->findPin(pin_name);
163+
if (pin == nullptr) {
164+
ADD_FAILURE() << "missing pin " << pin_name;
165165
return nullptr;
166166
}
167167

168-
sta::Vertex* endpoint = sta_->graph()->pinLoadVertex(latch_d);
168+
sta::Vertex* endpoint = sta_->graph()->pinLoadVertex(pin);
169169
if (endpoint == nullptr) {
170-
ADD_FAILURE() << "missing latch D load vertex";
170+
ADD_FAILURE() << "missing load vertex " << pin_name;
171171
return nullptr;
172172
}
173173
return endpoint;
@@ -186,7 +186,7 @@ class TestResizer : public tst::IntegratedFixture
186186
RepairTargetCollector collector(&resizer_);
187187
collector.init(0.0f);
188188

189-
sta::Vertex* endpoint = latchDataEndpoint();
189+
sta::Vertex* endpoint = loadVertex("enable_latch/D");
190190
if (endpoint == nullptr) {
191191
return {0.0f, 0.0f};
192192
}
@@ -197,6 +197,19 @@ class TestResizer : public tst::IntegratedFixture
197197
return {reported_slack, effective_slack};
198198
}
199199

200+
bool hasTargetPin(const std::vector<Target>& targets,
201+
const char* pin_name) const
202+
{
203+
for (const Target& target : targets) {
204+
if (target.driver_pin != nullptr
205+
&& std::string(db_network_->pathName(target.driver_pin))
206+
== pin_name) {
207+
return true;
208+
}
209+
}
210+
return false;
211+
}
212+
200213
sta::LibertyPort* findLibertyPort(sta::Instance* inst, const char* port_name)
201214
{
202215
std::unique_ptr<sta::InstancePinIterator> pin_iter{
@@ -286,4 +299,24 @@ TEST_F(TestResizer, CoveredBorrowKeepsReportedSlack)
286299
EXPECT_NEAR(slack.effective_slack, slack.reported_slack, one_ps);
287300
}
288301

302+
TEST_F(TestResizer, LatchThroughPathCollectsLatchDataFaninTargets)
303+
{
304+
setupTimeBorrowTiming(0.98);
305+
306+
RepairTargetCollector collector(&resizer_);
307+
collector.init(0.0f);
308+
309+
sta::Vertex* endpoint = loadVertex("gated_ff0/D");
310+
ASSERT_NE(endpoint, nullptr);
311+
312+
sta::Path* path = sta_->vertexWorstSlackPath(endpoint, sta::MinMax::max());
313+
ASSERT_NE(path, nullptr);
314+
315+
const std::vector<Target> targets
316+
= collector.collectPathDriverTargets(path, path->slack(sta_.get()));
317+
318+
EXPECT_TRUE(hasTargetPin(targets, "enable_buf0/Z"));
319+
EXPECT_TRUE(hasTargetPin(targets, "enable_buf1/Z"));
320+
}
321+
289322
} // namespace rsz

src/rsz/test/inferred_clock_gator_time_borrow.ok

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ BEFORE_BORROW 0.4545
1212
| Buffers | Gates | Buffers | Gates | Swaps | | | | | Endpts | St/EnPt
1313
------------------------------------------------------------------------------------------------------------------------------
1414
0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -0.076 | -0.1 | -0.3 | 7 | enable_latch/D
15-
16* | 1 | 5 | 1 | 0 | 0 | +13.6% | -0.048 | -0.1 | -0.2 | 7 | enable_latch/D
15+
28* | 1 | 5 | 1 | 0 | 0 | +13.6% | -0.048 | -0.1 | -0.2 | 7 | enable_latch/D
1616
final | 1 | 5 | 1 | 0 | 0 | +13.6% | -0.048 | -0.1 | -0.2 | 7 | enable_latch/D
1717
------------------------------------------------------------------------------------------------------------------------------
1818
[INFO RSZ-0059] Removed 1 buffers.

src/rsz/test/inferred_clock_gator_time_borrow_tns.ok

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ BEFORE_BORROW 0.4545
1313
| Buffers | Gates | Buffers | Gates | Swaps | | | | | Endpts | St/EnPt
1414
------------------------------------------------------------------------------------------------------------------------------
1515
0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -0.076 | -0.1 | -0.3 | 7 | enable_latch/D
16-
6* | 0 | 2 | 1 | 0 | 0 | +7.3% | -0.082 | -0.1 | -0.3 | 7 | enable_latch/D
17-
final | 0 | 2 | 1 | 0 | 0 | +7.3% | -0.082 | -0.1 | -0.3 | 7 | enable_latch/D
16+
6* | 1 | 7 | 1 | 0 | 0 | +46.4% | -0.049 | -0.1 | -0.2 | 7 | enable_latch/D
17+
final | 1 | 7 | 1 | 0 | 0 | +46.4% | -0.049 | -0.1 | -0.2 | 7 | enable_latch/D
1818
------------------------------------------------------------------------------------------------------------------------------
19+
[INFO RSZ-0059] Removed 1 buffers.
1920
[INFO RSZ-0040] Inserted 1 buffers.
20-
[INFO RSZ-0051] Resized 2 instances: 2 up, 0 up match, 0 down, 0 VT
21+
[INFO RSZ-0051] Resized 7 instances: 7 up, 0 up match, 0 down, 0 VT
2122
[WARNING RSZ-0062] Unable to repair all setup violations.
22-
AFTER_BORROW 0.4492
23+
AFTER_BORROW 0.4503

0 commit comments

Comments
 (0)