Skip to content

Commit 9ebc2b6

Browse files
committed
test: Add time-borrow effective slack coverage
Move the RSZ time-borrow regression checks into log-comparison tests so the golden .ok files validate the expected output directly. Add C++ coverage to TestResizer for covered and uncovered latch borrow cases, and wire the shared DEF fixture through both CMake and Bazel. Drop the pass/fail sentinel output from the Tcl tests because the golden log comparison already provides the pass condition. Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>
1 parent 260f401 commit 9ebc2b6

8 files changed

Lines changed: 231 additions & 175 deletions

src/rsz/test/BUILD

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,15 +250,15 @@ TESTS = [
250250
"global_sizing",
251251
"global_sizing_threads",
252252
"global_sizing_presize",
253+
"inferred_clock_gator_time_borrow",
254+
"inferred_clock_gator_time_borrow_covered",
253255
]
254256

255257
# From CMakeLists.txt or_integration_tests(PASSFAIL_TESTS
256258
PASSFAIL_TESTS = [
257259
# NOTE: cpp_tests excluded - it's a CMake-specific wrapper script that expects
258260
# build directory structure not compatible with Bazel
259261
# "cpp_tests",
260-
"inferred_clock_gator_time_borrow",
261-
"inferred_clock_gator_time_borrow_covered",
262262
"repair_setup_legacy_mt",
263263
"repair_setup_mt1",
264264
]
@@ -478,13 +478,25 @@ cc_test(
478478
name = "TestResizer",
479479
srcs = ["cpp/TestResizer.cc"],
480480
copts = ["-Isrc/rsz/src"],
481-
data = glob(["cpp/TestResizer*.v"]),
481+
data = glob(["cpp/TestResizer*.v"]) + [
482+
"inferred_clock_gator_time_borrow.def",
483+
],
482484
features = ["-layering_check"], # TODO: includes private headers
483485
deps = [
486+
"//src/ant",
487+
"//src/dbSta",
488+
"//src/dbSta:dbNetwork",
489+
"//src/dpl",
490+
"//src/est",
491+
"//src/grt",
484492
"//src/odb/src/db",
493+
"//src/odb/src/defin",
485494
"//src/rsz", # build_cleaner keep provides the private headers
486495
"//src/sta:opensta_lib",
496+
"//src/stt",
497+
"//src/tst",
487498
"//src/tst:integrated_fixture",
499+
"//src/utl",
488500
"@googletest//:gtest",
489501
"@googletest//:gtest_main",
490502
],

src/rsz/test/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,9 @@ or_integration_tests(
238238
global_sizing
239239
global_sizing_threads
240240
global_sizing_presize
241-
PASSFAIL_TESTS
242241
inferred_clock_gator_time_borrow
243242
inferred_clock_gator_time_borrow_covered
243+
PASSFAIL_TESTS
244244
repair_setup_legacy_mt
245245
repair_setup_mt1
246246
cpp_tests

src/rsz/test/cpp/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ target_link_libraries(TestResizer
100100
dpl_lib
101101
stt_lib
102102
odb
103+
defin
103104
tst
104105
${TCL_LIBRARY}
105106
)

src/rsz/test/cpp/TestResizer.cc

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,22 @@
33

44
#include <memory>
55
#include <string>
6+
#include <vector>
67

8+
#include "RepairTargetCollector.hh"
79
#include "gtest/gtest.h"
810
#include "odb/db.h"
11+
#include "odb/defin.h"
12+
#include "sta/Clock.hh"
13+
#include "sta/Graph.hh"
914
#include "sta/Liberty.hh"
15+
#include "sta/MinMax.hh"
16+
#include "sta/Mode.hh"
1017
#include "sta/NetworkClass.hh"
18+
#include "sta/Sdc.hh"
19+
#include "sta/Sta.hh"
20+
#include "sta/Transition.hh"
21+
#include "sta/Units.hh"
1122
#include "tst/IntegratedFixture.h"
1223

1324
namespace rsz {
@@ -35,6 +46,157 @@ class TestResizer : public tst::IntegratedFixture
3546
return modnet ? modnet->getName() : "None";
3647
}
3748

49+
float staTime(const float value) const
50+
{
51+
return sta_->units()->timeUnit()->userToSta(value);
52+
}
53+
54+
void readDefForTiming(const char* def_file)
55+
{
56+
odb::dbChip* chip = db_->getChip();
57+
if (chip == nullptr) {
58+
chip = odb::dbChip::create(db_.get(), db_->getTech());
59+
}
60+
61+
std::vector<odb::dbLib*> search_libs;
62+
for (odb::dbLib* db_lib : db_->getLibs()) {
63+
search_libs.push_back(db_lib);
64+
}
65+
66+
const std::string def_path = getFilePath(test_root_path_ + def_file);
67+
odb::defin def_reader(db_.get(), &logger_);
68+
def_reader.readChip(search_libs, def_path.c_str(), chip);
69+
70+
sta_->postReadDb(db_.get());
71+
block_ = chip->getBlock();
72+
sta_->postReadDef(block_);
73+
}
74+
75+
sta::Pin* findTopPin(const char* port_name) const
76+
{
77+
sta::Instance* top_inst = db_network_->topInstance();
78+
sta::Cell* top_cell = db_network_->cell(top_inst);
79+
if (top_cell == nullptr) {
80+
ADD_FAILURE() << "missing top cell";
81+
return nullptr;
82+
}
83+
84+
sta::Port* port = db_network_->findPort(top_cell, port_name);
85+
if (port == nullptr) {
86+
ADD_FAILURE() << "missing top port " << port_name;
87+
return nullptr;
88+
}
89+
90+
sta::Pin* pin = db_network_->findPin(top_inst, port);
91+
if (pin == nullptr) {
92+
ADD_FAILURE() << "missing top pin " << port_name;
93+
return nullptr;
94+
}
95+
return pin;
96+
}
97+
98+
void makeClock(const char* clock_name, sta::Pin* pin) const
99+
{
100+
sta::PinSet pins(db_network_);
101+
if (pin != nullptr) {
102+
pins.insert(pin);
103+
}
104+
105+
const float period = staTime(1.0);
106+
sta::FloatSeq waveform;
107+
waveform.push_back(0.0);
108+
waveform.push_back(period / 2.0);
109+
110+
sta_->makeClock(
111+
clock_name, pins, false, period, waveform, "", sta_->cmdMode());
112+
}
113+
114+
void setInputDelay(const char* port_name,
115+
const char* clock_name,
116+
const float delay) const
117+
{
118+
sta::Sdc* sdc = sta_->cmdMode()->sdc();
119+
sta::Clock* clock = sdc->findClock(clock_name);
120+
ASSERT_NE(clock, nullptr);
121+
122+
sta::Pin* pin = findTopPin(port_name);
123+
ASSERT_NE(pin, nullptr);
124+
125+
sta_->setInputDelay(pin,
126+
sta::RiseFallBoth::riseFall(),
127+
clock,
128+
sta::RiseFall::rise(),
129+
nullptr,
130+
false,
131+
false,
132+
sta::MinMaxAll::all(),
133+
true,
134+
staTime(delay),
135+
sdc);
136+
}
137+
138+
void setupTimeBorrowTiming(const float input_delay)
139+
{
140+
readDefForTiming("inferred_clock_gator_time_borrow.def");
141+
142+
sta::Pin* clk_pin = findTopPin("clk");
143+
ASSERT_NE(clk_pin, nullptr);
144+
145+
makeClock("clk", clk_pin);
146+
makeClock("vclk", nullptr);
147+
148+
sta::Clock* clk = sta_->cmdMode()->sdc()->findClock("clk");
149+
ASSERT_NE(clk, nullptr);
150+
sta_->setPropagatedClock(clk, sta_->cmdMode());
151+
setInputDelay("en_in", "vclk", input_delay);
152+
153+
sta_->ensureGraph();
154+
sta_->ensureLevelized();
155+
resizer_.initBlock();
156+
ep_.estimateWireParasitics();
157+
sta_->updateTiming(true);
158+
}
159+
160+
sta::Vertex* latchDataEndpoint() const
161+
{
162+
sta::Pin* latch_d = db_network_->findPin("enable_latch/D");
163+
if (latch_d == nullptr) {
164+
ADD_FAILURE() << "missing latch D pin";
165+
return nullptr;
166+
}
167+
168+
sta::Vertex* endpoint = sta_->graph()->pinLoadVertex(latch_d);
169+
if (endpoint == nullptr) {
170+
ADD_FAILURE() << "missing latch D load vertex";
171+
return nullptr;
172+
}
173+
return endpoint;
174+
}
175+
176+
struct SlackPair
177+
{
178+
sta::Slack reported_slack;
179+
sta::Slack effective_slack;
180+
};
181+
182+
SlackPair latchDataSlack(const float input_delay)
183+
{
184+
setupTimeBorrowTiming(input_delay);
185+
186+
RepairTargetCollector collector(&resizer_);
187+
collector.init(0.0f);
188+
189+
sta::Vertex* endpoint = latchDataEndpoint();
190+
if (endpoint == nullptr) {
191+
return {0.0f, 0.0f};
192+
}
193+
194+
const sta::Slack reported_slack = sta_->slack(endpoint, sta::MinMax::max());
195+
const sta::Slack effective_slack
196+
= collector.getEndpointEffectiveSlack(endpoint);
197+
return {reported_slack, effective_slack};
198+
}
199+
38200
sta::LibertyPort* findLibertyPort(sta::Instance* inst, const char* port_name)
39201
{
40202
std::unique_ptr<sta::InstancePinIterator> pin_iter{
@@ -96,4 +258,32 @@ TEST_F(TestResizer, SwapPinsFeedthroughModNet)
96258
writeAndCompareVerilogOutputFile(test_name, test_name + "_post.v");
97259
}
98260

261+
// A large virtual-clock input delay (0.98) forces the enable latch to borrow
262+
// more time than the downstream logic can absorb (uncovered borrow). In that
263+
// case getEndpointEffectiveSlack() must subtract the uncovered borrow, yielding
264+
// an effective slack strictly worse than the reported slack so that
265+
// repair_timing targets the latch D endpoint.
266+
TEST_F(TestResizer, UncoveredBorrowReducesEffectiveSlack)
267+
{
268+
const SlackPair slack = latchDataSlack(0.98);
269+
const float one_ps = staTime(0.001);
270+
271+
EXPECT_LE(slack.reported_slack, 0.0);
272+
EXPECT_LT(slack.effective_slack, slack.reported_slack - one_ps);
273+
}
274+
275+
// A smaller virtual-clock input delay (0.80) leaves enough downstream setup
276+
// margin to fully cover the latch borrow (covered borrow).
277+
// getEndpointEffectiveSlack() must then leave the reported slack unchanged, so
278+
// that repair_timing does not spuriously optimize the already-covered latch D
279+
// endpoint.
280+
TEST_F(TestResizer, CoveredBorrowKeepsReportedSlack)
281+
{
282+
const SlackPair slack = latchDataSlack(0.80);
283+
const float one_ps = staTime(0.001);
284+
285+
EXPECT_NEAR(slack.reported_slack, 0.0, one_ps);
286+
EXPECT_NEAR(slack.effective_slack, slack.reported_slack, one_ps);
287+
}
288+
99289
} // namespace rsz

0 commit comments

Comments
 (0)