Skip to content

Commit 2aa25ef

Browse files
committed
rsz: Walk chained latch fanin paths
Extend latch-through path handling from a single latch D-side segment to a chained fanin walk. This lets setup repair collect and rank drivers through cascaded transparent latches instead of stopping after the first latch data path. Add a shared latch fanin visitor, update repair target collection and legacy setup repair to use it, and cover the behavior with a two-latch DEF plus C++ and Tcl regressions. Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
1 parent 2355a48 commit 2aa25ef

11 files changed

Lines changed: 336 additions & 67 deletions

src/rsz/src/OptimizerTypes.cc

Lines changed: 40 additions & 2 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,8 +197,7 @@ const sta::TimingArc* findMatchingTimingArc(const sta::TimingArc* reference,
195197
reference, candidate, ArcMatchMode::kExact, nullptr);
196198
}
197199

198-
const sta::Path* latchDataPath(const sta::PathExpanded& expanded,
199-
const sta::StaState*)
200+
const sta::Path* latchDataPath(const sta::PathExpanded& expanded)
200201
{
201202
const sta::Path* d_path = nullptr;
202203
const sta::Path* q_path = nullptr;
@@ -205,6 +206,43 @@ const sta::Path* latchDataPath(const sta::PathExpanded& expanded,
205206
return d_path;
206207
}
207208

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+
208246
bool Target::canBePathDriver() const
209247
{
210248
return (views & kPathDriverView) != 0 && driver_pin != nullptr

src/rsz/src/OptimizerTypes.hh

Lines changed: 18 additions & 2 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>
@@ -134,8 +135,23 @@ const sta::TimingArc* findMatchingTimingArc(const sta::TimingArc* reference,
134135
const sta::TimingArcSet* candidate);
135136

136137
// Return the latch D-side path attached to an expanded latch-through path.
137-
const sta::Path* latchDataPath(const sta::PathExpanded& expanded,
138-
const sta::StaState* sta);
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);
139155

140156
// One timing arc that can contribute to the driver's output slew merge for the
141157
// selected output transition. STA merges output slew across all such arcs

src/rsz/src/RepairTargetCollector.cc

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -932,14 +932,17 @@ std::vector<Target> RepairTargetCollector::collectPathDriverTargets(
932932
targets.reserve(expanded.size());
933933
collectExpandedPathDriverTargets(path, expanded, path_slack, targets);
934934

935-
// A latch-through path starts at latch Q in OpenSTA, with the latch D path
936-
// stored as side context. Expand that D path separately so repair_timing can
937-
// optimize the logic that consumed the borrowed time.
938-
const sta::Path* d_path = latchDataPath(expanded, sta_);
939-
if (d_path != nullptr) {
940-
sta::PathExpanded d_expanded(d_path, sta_);
941-
collectExpandedPathDriverTargets(d_path, d_expanded, path_slack, targets);
942-
}
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+
});
943946

944947
return targets;
945948
}
@@ -1259,11 +1262,13 @@ set<const sta::Pin*> RepairTargetCollector::collectPinsByPathEndpoint(
12591262
}
12601263
}
12611264

1262-
const sta::Path* d_path = latchDataPath(expanded, sta_);
1263-
if (d_path != nullptr) {
1264-
sta::PathExpanded d_expanded(d_path, sta_);
1265-
const size_t old_size = viol_pins.size();
1266-
collectExpandedPathDriverPins(d_expanded, viol_pins);
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) {
12671272
debugPrint(logger_,
12681273
RSZ,
12691274
"violator_collector",

src/rsz/src/policy/SetupLegacyBase.cc

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -452,32 +452,26 @@ bool SetupLegacyBase::makePinTargetOnPath(const sta::Pin* pin,
452452
return false;
453453
}
454454

455+
// Search the main path first, then the latch D fanin path of a
456+
// latch-through path.
455457
sta::PathExpanded expanded(path, sta_);
456-
if (makePinTargetInExpandedPath(pin, path, expanded, focus_slack, target)) {
457-
return true;
458-
}
459-
460-
const sta::Path* d_path = latchDataPath(expanded, sta_);
461-
if (d_path == nullptr) {
462-
return false;
463-
}
464-
465-
sta::PathExpanded d_expanded(d_path, sta_);
466-
return makePinTargetInExpandedPath(
467-
pin, d_path, d_expanded, focus_slack, target);
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+
});
468466
}
469467

470468
bool SetupLegacyBase::makePinTargetInExpandedPath(const sta::Pin* pin,
469+
sta::Vertex* vertex,
471470
const sta::Path* path,
472471
sta::PathExpanded& expanded,
473472
const sta::Slack focus_slack,
474473
Target& target) const
475474
{
476-
sta::Vertex* vertex = graph_->pinDrvrVertex(pin);
477-
if (vertex == nullptr) {
478-
return false;
479-
}
480-
481475
const int start_index = static_cast<int>(expanded.startIndex());
482476
const int path_count = static_cast<int>(expanded.size());
483477
for (int index = start_index; index < path_count; index++) {
@@ -754,51 +748,46 @@ bool SetupLegacyBase::repairPath(sta::Path* path,
754748
return false;
755749
}
756750

757-
const sta::Scene* corner = path->scene(sta_);
758751
if (path->minMax(sta_) != resizer_.max_) {
759752
logger_->error(utl::RSZ,
760753
kMsgRepairSetupExpectedMaxPath,
761754
"repairSetup expects max delay path");
762755
return false;
763756
}
764757

765-
const std::vector<std::pair<int, sta::Delay>> load_delays
766-
= rankPathDrivers(expanded, corner, corner->libertyIndex(resizer_.max_));
767758
const int repairs_per_pass = repairBudget(path_slack, force_single_repair);
768759

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+
769783
debugPrint(logger_,
770784
RSZ,
771785
"repair_setup",
772786
3,
773-
"Path slack: {}, repairs: {}, load_delays: {}",
787+
"Path slack: {}, repairs: {}, ranked_targets: {}",
774788
delayAsString(path_slack, 3, sta_),
775789
repairs_per_pass,
776-
load_delays.size());
777-
778-
// Construct target vector
779-
std::vector<std::pair<Target, sta::Delay>> ranked_targets;
780-
ranked_targets.reserve(load_delays.size());
781-
for (const std::pair<int, sta::Delay>& load_delay : load_delays) {
782-
Target target;
783-
makePathDriverTarget(path, expanded, load_delay.first, path_slack, target);
784-
ranked_targets.emplace_back(std::move(target), load_delay.second);
785-
}
786-
787-
const sta::Path* d_path = latchDataPath(expanded, sta_);
788-
if (d_path != nullptr) {
789-
sta::PathExpanded d_expanded(d_path, sta_);
790-
const sta::Scene* d_corner = d_path->scene(sta_);
791-
const std::vector<std::pair<int, sta::Delay>> d_load_delays
792-
= rankPathDrivers(
793-
d_expanded, d_corner, d_corner->libertyIndex(resizer_.max_));
794-
ranked_targets.reserve(ranked_targets.size() + d_load_delays.size());
795-
for (const std::pair<int, sta::Delay>& load_delay : d_load_delays) {
796-
Target target;
797-
makePathDriverTarget(
798-
d_path, d_expanded, load_delay.first, path_slack, target);
799-
ranked_targets.emplace_back(std::move(target), load_delay.second);
800-
}
801-
}
790+
ranked_targets.size());
802791

803792
std::ranges::stable_sort(ranked_targets,
804793
[](const std::pair<Target, sta::Delay>& lhs,

src/rsz/src/policy/SetupLegacyBase.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ class SetupLegacyBase : public OptimizationPolicy
141141
sta::Slack focus_slack,
142142
Target& target) const;
143143
bool makePinTargetInExpandedPath(const sta::Pin* pin,
144+
sta::Vertex* vertex,
144145
const sta::Path* path,
145146
sta::PathExpanded& expanded,
146147
sta::Slack focus_slack,

src/rsz/test/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ TESTS = [
254254
"inferred_clock_gator_time_borrow_covered",
255255
"inferred_clock_gator_time_borrow_repair_pin",
256256
"inferred_clock_gator_time_borrow_tns",
257+
"latch_borrow_chain",
257258
]
258259

259260
# From CMakeLists.txt or_integration_tests(PASSFAIL_TESTS
@@ -482,6 +483,7 @@ cc_test(
482483
copts = ["-Isrc/rsz/src"],
483484
data = glob(["cpp/TestResizer*.v"]) + [
484485
"inferred_clock_gator_time_borrow.def",
486+
"latch_borrow_chain.def",
485487
],
486488
features = ["-layering_check"], # TODO: includes private headers
487489
deps = [

src/rsz/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ or_integration_tests(
242242
inferred_clock_gator_time_borrow_covered
243243
inferred_clock_gator_time_borrow_repair_pin
244244
inferred_clock_gator_time_borrow_tns
245+
latch_borrow_chain
245246
PASSFAIL_TESTS
246247
repair_setup_legacy_mt
247248
repair_setup_mt1

src/rsz/test/cpp/TestResizer.cc

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,9 @@ class TestResizer : public tst::IntegratedFixture
135135
sdc);
136136
}
137137

138-
void setupTimeBorrowTiming(const float input_delay)
138+
void setupTimeBorrowTiming(const char* def_file, const float input_delay)
139139
{
140-
readDefForTiming("inferred_clock_gator_time_borrow.def");
140+
readDefForTiming(def_file);
141141

142142
sta::Pin* clk_pin = findTopPin("clk");
143143
ASSERT_NE(clk_pin, nullptr);
@@ -249,7 +249,7 @@ TEST_F(TestResizer, SwapPinsFeedthroughModNet)
249249

250250
TEST_F(TestResizer, LatchThroughPathCollectsLatchDataFaninTargets)
251251
{
252-
setupTimeBorrowTiming(0.98);
252+
setupTimeBorrowTiming("inferred_clock_gator_time_borrow.def", 0.98);
253253

254254
RepairTargetCollector collector(&resizer_);
255255
collector.init(0.0f);
@@ -267,4 +267,24 @@ TEST_F(TestResizer, LatchThroughPathCollectsLatchDataFaninTargets)
267267
EXPECT_TRUE(hasTargetPin(targets, "enable_buf1/Z"));
268268
}
269269

270+
TEST_F(TestResizer, ChainedLatchFaninTargets)
271+
{
272+
setupTimeBorrowTiming("latch_borrow_chain.def", 0.84);
273+
274+
RepairTargetCollector collector(&resizer_);
275+
collector.init(0.0f);
276+
277+
sta::Vertex* endpoint = loadVertex("gated_ff0/D");
278+
ASSERT_NE(endpoint, nullptr);
279+
280+
sta::Path* path = sta_->vertexWorstSlackPath(endpoint, sta::MinMax::max());
281+
ASSERT_NE(path, nullptr);
282+
283+
const std::vector<Target> targets
284+
= collector.collectPathDriverTargets(path, path->slack(sta_.get()));
285+
286+
EXPECT_TRUE(hasTargetPin(targets, "mid_buf0/Z"));
287+
EXPECT_TRUE(hasTargetPin(targets, "deep_buf0/Z"));
288+
}
289+
270290
} // namespace rsz

0 commit comments

Comments
 (0)