Skip to content

Commit 1945bd8

Browse files
authored
Merge pull request #10825 from The-OpenROAD-Project-staging/secure-rsz-time-borrow-opt
rsz: Optimize latch time-borrow setup paths
2 parents 89bf707 + 51c017c commit 1945bd8

29 files changed

Lines changed: 997 additions & 60 deletions

src/rsz/src/OptimizerTypes.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#include "OptimizerTypes.hh"
55

6+
#include <functional>
7+
#include <set>
68
#include <string>
79

810
#include "rsz/Resizer.hh"
@@ -195,6 +197,52 @@ const sta::TimingArc* findMatchingTimingArc(const sta::TimingArc* reference,
195197
reference, candidate, ArcMatchMode::kExact, nullptr);
196198
}
197199

200+
const sta::Path* latchDataPath(const sta::PathExpanded& expanded)
201+
{
202+
const sta::Path* d_path = nullptr;
203+
const sta::Path* q_path = nullptr;
204+
sta::Edge* d_q_edge = nullptr;
205+
expanded.latchPaths(d_path, q_path, d_q_edge);
206+
return d_path;
207+
}
208+
209+
bool visitLatchFaninSegments(
210+
const sta::PathExpanded& expanded,
211+
const sta::StaState* sta,
212+
const std::function<bool(const sta::Path*, sta::PathExpanded&)>& visitor)
213+
{
214+
// Guard against accidental latch loops while following D-side fanin chains.
215+
std::set<const sta::Pin*> visited_latch_d;
216+
const sta::Path* d_path = latchDataPath(expanded);
217+
while (d_path != nullptr) {
218+
const sta::Pin* d_pin = d_path->pin(sta);
219+
if (visited_latch_d.contains(d_pin)) {
220+
break;
221+
}
222+
visited_latch_d.insert(d_pin);
223+
224+
sta::PathExpanded d_expanded(d_path, sta);
225+
if (visitor(d_path, d_expanded)) {
226+
return true;
227+
}
228+
d_path = latchDataPath(d_expanded);
229+
}
230+
return false;
231+
}
232+
233+
bool visitPathSegments(
234+
const sta::Path* path,
235+
sta::PathExpanded& expanded,
236+
const sta::StaState* sta,
237+
const std::function<bool(const sta::Path*, sta::PathExpanded&)>& visitor)
238+
{
239+
if (visitor(path, expanded)) {
240+
return true;
241+
}
242+
243+
return visitLatchFaninSegments(expanded, sta, visitor);
244+
}
245+
198246
bool Target::canBePathDriver() const
199247
{
200248
return (views & kPathDriverView) != 0 && driver_pin != nullptr

src/rsz/src/OptimizerTypes.hh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <array>
77
#include <cstddef>
88
#include <cstdint>
9+
#include <functional>
910
#include <memory>
1011
#include <optional>
1112
#include <stdexcept>
@@ -27,6 +28,7 @@ class Pin;
2728
class Pvt;
2829
class RiseFall;
2930
class Scene;
31+
class StaState;
3032
class TimingArc;
3133
class TimingArcSet;
3234
class Vertex;
@@ -132,6 +134,25 @@ struct SelectedArc
132134
const sta::TimingArc* findMatchingTimingArc(const sta::TimingArc* reference,
133135
const sta::TimingArcSet* candidate);
134136

137+
// Return the latch D-side path attached to an expanded latch-through path.
138+
const sta::Path* latchDataPath(const sta::PathExpanded& expanded);
139+
140+
// Walk the chained latch D-side fanin segments of an expanded path. Each
141+
// visitor PathExpanded reference is valid only during that visitor call.
142+
// A visitor returns true to stop walking and false to continue.
143+
bool visitLatchFaninSegments(
144+
const sta::PathExpanded& expanded,
145+
const sta::StaState* sta,
146+
const std::function<bool(const sta::Path*, sta::PathExpanded&)>& visitor);
147+
148+
// Visit the main expanded path and every chained latch D-side fanin segment.
149+
// A visitor returns true to stop walking and false to continue.
150+
bool visitPathSegments(
151+
const sta::Path* path,
152+
sta::PathExpanded& expanded,
153+
const sta::StaState* sta,
154+
const std::function<bool(const sta::Path*, sta::PathExpanded&)>& visitor);
155+
135156
// One timing arc that can contribute to the driver's output slew merge for the
136157
// selected output transition. STA merges output slew across all such arcs
137158
// before the next stage consumes it.

src/rsz/src/RepairTargetCollector.cc

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ const char* RepairTargetCollector::getEnumString(ViolatorSortType sort_type)
9191
void RepairTargetCollector::printViolators(int numPrint = 0) const
9292
{
9393
if (violating_pins_.empty()) {
94-
logger_->info(RSZ, 8, "No violating pins found.");
94+
debugPrint(
95+
logger_, RSZ, "violator_collector", 1, "No violating pins found.");
9596
return;
9697
}
9798

@@ -812,9 +813,9 @@ void RepairTargetCollector::collectViolatingEndpoints()
812813

813814
const sta::VertexSet& endpoints = sta_->endpoints();
814815
for (sta::Vertex* endpoint : endpoints) {
815-
const sta::Slack endpoint_slack = sta_->slack(endpoint, max_);
816-
if (sta::fuzzyLess(endpoint_slack, slack_margin_)) {
817-
violating_endpoints_.emplace_back(endpoint->pin(), endpoint_slack);
816+
const sta::Slack slack = sta_->slack(endpoint, max_);
817+
if (sta::fuzzyLess(slack, slack_margin_)) {
818+
violating_endpoints_.emplace_back(endpoint->pin(), slack);
818819
}
819820
}
820821

@@ -877,6 +878,47 @@ sta::Path* RepairTargetCollector::findWorstSlackPath(
877878
return sta_->vertexWorstSlackPath(endpoint, max_);
878879
}
879880

881+
void RepairTargetCollector::collectExpandedPathDriverTargets(
882+
const sta::Path* endpoint_path,
883+
const sta::PathExpanded& expanded,
884+
const sta::Slack path_slack,
885+
std::vector<Target>& targets) const
886+
{
887+
const int start_index = static_cast<int>(expanded.startIndex());
888+
const int path_count = static_cast<int>(expanded.size());
889+
for (int index = start_index; index < path_count; ++index) {
890+
const sta::Path* driver_path = expanded.path(index);
891+
sta::Vertex* vertex = driver_path->vertex(sta_);
892+
sta::Pin* pin = driver_path->pin(sta_);
893+
if (vertex == nullptr || pin == nullptr || !vertex->isDriver(network_)
894+
|| network_->isTopLevelPort(pin)) {
895+
continue;
896+
}
897+
898+
targets.push_back(makePathDriverTarget(
899+
endpoint_path, expanded, index, path_slack, *resizer_));
900+
}
901+
}
902+
903+
void RepairTargetCollector::collectExpandedPathDriverPins(
904+
const sta::PathExpanded& expanded,
905+
set<const sta::Pin*>& pins) const
906+
{
907+
const int start_index = static_cast<int>(expanded.startIndex());
908+
const int path_count = static_cast<int>(expanded.size());
909+
for (int index = start_index; index < path_count; ++index) {
910+
const sta::Path* driver_path = expanded.path(index);
911+
sta::Vertex* vertex = driver_path->vertex(sta_);
912+
const sta::Pin* pin = driver_path->pin(sta_);
913+
if (vertex == nullptr || pin == nullptr || !vertex->isDriver(network_)
914+
|| network_->isTopLevelPort(pin)
915+
|| sta_->isClock(pin, sta_->cmdMode())) {
916+
continue;
917+
}
918+
pins.insert(pin);
919+
}
920+
}
921+
880922
std::vector<Target> RepairTargetCollector::collectPathDriverTargets(
881923
sta::Path* path,
882924
const sta::Slack path_slack) const
@@ -888,18 +930,19 @@ std::vector<Target> RepairTargetCollector::collectPathDriverTargets(
888930

889931
sta::PathExpanded expanded(path, sta_);
890932
targets.reserve(expanded.size());
891-
for (int index = expanded.startIndex(); index < expanded.size(); ++index) {
892-
const sta::Path* driver_path = expanded.path(index);
893-
sta::Vertex* vertex = driver_path->vertex(sta_);
894-
sta::Pin* pin = driver_path->pin(sta_);
895-
if (vertex == nullptr || pin == nullptr || !vertex->isDriver(network_)
896-
|| network_->isTopLevelPort(pin)) {
897-
continue;
898-
}
899-
900-
targets.push_back(
901-
makePathDriverTarget(path, expanded, index, path_slack, *resizer_));
902-
}
933+
collectExpandedPathDriverTargets(path, expanded, path_slack, targets);
934+
935+
// A latch-through path starts at latch Q in OpenSTA, with each latch D path
936+
// stored as side context. Expand those D paths separately so repair_timing
937+
// can optimize the logic that consumed the borrowed time.
938+
visitLatchFaninSegments(
939+
expanded,
940+
sta_,
941+
[&](const sta::Path* d_path, sta::PathExpanded& d_expanded) {
942+
collectExpandedPathDriverTargets(
943+
d_path, d_expanded, path_slack, targets);
944+
return false;
945+
});
903946

904947
return targets;
905948
}
@@ -1218,6 +1261,21 @@ set<const sta::Pin*> RepairTargetCollector::collectPinsByPathEndpoint(
12181261
}
12191262
}
12201263
}
1264+
1265+
const size_t old_size = viol_pins.size();
1266+
visitLatchFaninSegments(
1267+
expanded, sta_, [&](const sta::Path*, sta::PathExpanded& d_expanded) {
1268+
collectExpandedPathDriverPins(d_expanded, viol_pins);
1269+
return false;
1270+
});
1271+
if (viol_pins.size() > old_size) {
1272+
debugPrint(logger_,
1273+
RSZ,
1274+
"violator_collector",
1275+
4,
1276+
"Added {} latch D fanin pins from latch-through path",
1277+
viol_pins.size() - old_size);
1278+
}
12211279
debugPrint(logger_, RSZ, "violator_collector", 5, "\n");
12221280
}
12231281

@@ -2206,7 +2264,6 @@ void RepairTargetCollector::setToEndpoint(int index)
22062264
current_endpoint_index_ = index;
22072265
const auto& end_slack_pair = violating_endpoints_[current_endpoint_index_];
22082266
current_endpoint_ = graph_->pinLoadVertex(end_slack_pair.first);
2209-
current_end_original_slack_ = end_slack_pair.second;
22102267
}
22112268

22122269
void RepairTargetCollector::setToStartpoint(int index)
@@ -2215,7 +2272,6 @@ void RepairTargetCollector::setToStartpoint(int index)
22152272
const auto& start_slack_pair
22162273
= violating_startpoints_[current_startpoint_index_];
22172274
current_startpoint_ = graph_->pinLoadVertex(start_slack_pair.first);
2218-
// Note: For startpoints, we don't use current_end_original_slack_
22192275
}
22202276

22212277
void RepairTargetCollector::advanceToNextEndpoint()

src/rsz/src/RepairTargetCollector.hh

Lines changed: 10 additions & 5 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;
@@ -94,10 +98,6 @@ class RepairTargetCollector
9498
void setToEndpoint(int index);
9599
sta::Vertex* getCurrentEndpoint() const { return current_endpoint_; }
96100
sta::Slack getCurrentEndpointSlack() const;
97-
sta::Slack getCurrentEndpointOriginalSlack() const
98-
{
99-
return current_end_original_slack_;
100-
}
101101
int getCurrentEndpointIndex() const { return current_endpoint_index_; }
102102
int getMaxEndpointCount() const { return violating_endpoints_.size(); }
103103
int getCurrentPass() const;
@@ -286,6 +286,12 @@ class RepairTargetCollector
286286
void sortByHeuristic(float load_delay_threshold = 0.0);
287287
std::map<const sta::Pin*, sta::Delay> getLocalTns() const;
288288
sta::Delay getLocalPinTns(const sta::Pin* pin) const;
289+
void collectExpandedPathDriverTargets(const sta::Path* endpoint_path,
290+
const sta::PathExpanded& expanded,
291+
sta::Slack path_slack,
292+
std::vector<Target>& targets) const;
293+
void collectExpandedPathDriverPins(const sta::PathExpanded& expanded,
294+
set<const sta::Pin*>& pins) const;
289295

290296
// === Cone traversal helpers ==============================================
291297
// Helper functions for cone-based collection
@@ -333,7 +339,6 @@ class RepairTargetCollector
333339
// Current endpoint iteration state
334340
bool iteration_began_;
335341
sta::Vertex* current_endpoint_;
336-
sta::Slack current_end_original_slack_;
337342
int current_endpoint_index_;
338343

339344
// === Startpoint iteration state ==========================================

src/rsz/src/policy/SetupLegacyBase.cc

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ bool SetupLegacyBase::repairSetupPin(const sta::Pin* end_pin)
8888
}
8989

9090
initializeSetupServices();
91+
target_collector_->init(config_.setup_slack_margin);
9192
setup_context_.max_repairs_per_pass = 1;
9293

9394
sta::Vertex* end_vertex = graph_->pinLoadVertex(end_pin);
@@ -451,8 +452,29 @@ bool SetupLegacyBase::makePinTargetOnPath(const sta::Pin* pin,
451452
return false;
452453
}
453454

455+
// Search the main path first, then the latch D fanin path of a
456+
// latch-through path.
454457
sta::PathExpanded expanded(path, sta_);
455-
for (int index = expanded.startIndex(); index < expanded.size(); index++) {
458+
return visitPathSegments(
459+
path,
460+
expanded,
461+
sta_,
462+
[&](const sta::Path* seg_path, sta::PathExpanded& seg_expanded) {
463+
return makePinTargetInExpandedPath(
464+
pin, vertex, seg_path, seg_expanded, focus_slack, target);
465+
});
466+
}
467+
468+
bool SetupLegacyBase::makePinTargetInExpandedPath(const sta::Pin* pin,
469+
sta::Vertex* vertex,
470+
const sta::Path* path,
471+
sta::PathExpanded& expanded,
472+
const sta::Slack focus_slack,
473+
Target& target) const
474+
{
475+
const int start_index = static_cast<int>(expanded.startIndex());
476+
const int path_count = static_cast<int>(expanded.size());
477+
for (int index = start_index; index < path_count; index++) {
456478
const sta::Path* driver_path = expanded.path(index);
457479
sta::Vertex* path_vertex = driver_path->vertex(sta_);
458480
const sta::Pin* path_pin = driver_path->pin(sta_);
@@ -726,35 +748,57 @@ bool SetupLegacyBase::repairPath(sta::Path* path,
726748
return false;
727749
}
728750

729-
const sta::Scene* corner = path->scene(sta_);
730751
if (path->minMax(sta_) != resizer_.max_) {
731752
logger_->error(utl::RSZ,
732753
kMsgRepairSetupExpectedMaxPath,
733754
"repairSetup expects max delay path");
734755
return false;
735756
}
736757

737-
const auto load_delays
738-
= rankPathDrivers(expanded, corner, corner->libertyIndex(resizer_.max_));
739758
const int repairs_per_pass = repairBudget(path_slack, force_single_repair);
740759

760+
// Rank drivers on the main path and, for latch-through paths, on the latch
761+
// D fanin path, then merge both segments into a single ranking.
762+
std::vector<std::pair<Target, sta::Delay>> ranked_targets;
763+
visitPathSegments(
764+
path,
765+
expanded,
766+
sta_,
767+
[&](const sta::Path* seg_path, sta::PathExpanded& seg_expanded) {
768+
const sta::Scene* seg_corner = seg_path->scene(sta_);
769+
const std::vector<std::pair<int, sta::Delay>> load_delays
770+
= rankPathDrivers(seg_expanded,
771+
seg_corner,
772+
seg_corner->libertyIndex(resizer_.max_));
773+
ranked_targets.reserve(ranked_targets.size() + load_delays.size());
774+
for (const std::pair<int, sta::Delay>& load_delay : load_delays) {
775+
Target target;
776+
makePathDriverTarget(
777+
seg_path, seg_expanded, load_delay.first, path_slack, target);
778+
ranked_targets.emplace_back(std::move(target), load_delay.second);
779+
}
780+
return false;
781+
});
782+
741783
debugPrint(logger_,
742784
RSZ,
743785
"repair_setup",
744786
3,
745-
"Path slack: {}, repairs: {}, load_delays: {}",
787+
"Path slack: {}, repairs: {}, ranked_targets: {}",
746788
delayAsString(path_slack, 3, sta_),
747789
repairs_per_pass,
748-
load_delays.size());
790+
ranked_targets.size());
791+
792+
std::ranges::stable_sort(ranked_targets,
793+
[](const std::pair<Target, sta::Delay>& lhs,
794+
const std::pair<Target, sta::Delay>& rhs) {
795+
return lhs.second > rhs.second;
796+
});
749797

750-
// Construct target vector
751798
std::vector<Target> targets;
752-
targets.reserve(load_delays.size());
753-
for (const auto& [drvr_index, ignored] : load_delays) {
754-
static_cast<void>(ignored);
755-
Target target;
756-
makePathDriverTarget(path, expanded, drvr_index, path_slack, target);
757-
targets.push_back(std::move(target));
799+
targets.reserve(ranked_targets.size());
800+
for (std::pair<Target, sta::Delay>& ranked_target : ranked_targets) {
801+
targets.push_back(std::move(ranked_target.first));
758802
}
759803

760804
// Prewarm for legacy MT policy

0 commit comments

Comments
 (0)