#include #include #include #include "DelayCalc.hh" #include "ArcDelayCalc.hh" #include "DelayFloat.hh" #include "dcalc/FindRoot.hh" namespace sta { class DcalcRegistryTest : public ::testing::Test { protected: void SetUp() override { registerDelayCalcs(); } void TearDown() override { deleteDelayCalcs(); } }; TEST_F(DcalcRegistryTest, BuiltinCalcsRegistered) { EXPECT_TRUE(isDelayCalcName("unit")); EXPECT_TRUE(isDelayCalcName("lumped_cap")); EXPECT_TRUE(isDelayCalcName("dmp_ceff_elmore")); EXPECT_TRUE(isDelayCalcName("dmp_ceff_two_pole")); EXPECT_TRUE(isDelayCalcName("arnoldi")); EXPECT_TRUE(isDelayCalcName("ccs_ceff")); EXPECT_TRUE(isDelayCalcName("prima")); } TEST_F(DcalcRegistryTest, UnknownCalcNotRegistered) { EXPECT_FALSE(isDelayCalcName("nonexistent")); EXPECT_FALSE(isDelayCalcName("")); } TEST_F(DcalcRegistryTest, DelayCalcNamesCount) { StringSeq names = delayCalcNames(); EXPECT_EQ(names.size(), 7); } TEST_F(DcalcRegistryTest, MakeUnknownCalcReturnsNull) { ArcDelayCalc *calc = makeDelayCalc("nonexistent", nullptr); EXPECT_EQ(calc, nullptr); } //////////////////////////////////////////////////////////////// class ArcDcalcArgTest : public ::testing::Test {}; TEST_F(ArcDcalcArgTest, DefaultConstruction) { ArcDcalcArg arg; EXPECT_EQ(arg.inPin(), nullptr); EXPECT_EQ(arg.drvrPin(), nullptr); EXPECT_EQ(arg.edge(), nullptr); EXPECT_EQ(arg.arc(), nullptr); EXPECT_FLOAT_EQ(arg.loadCap(), 0.0f); EXPECT_FLOAT_EQ(arg.inputDelay(), 0.0f); EXPECT_EQ(arg.parasitic(), nullptr); } TEST_F(ArcDcalcArgTest, SetLoadCap) { ArcDcalcArg arg; arg.setLoadCap(1.5e-12f); EXPECT_FLOAT_EQ(arg.loadCap(), 1.5e-12f); } TEST_F(ArcDcalcArgTest, SetInputDelay) { ArcDcalcArg arg; arg.setInputDelay(0.5e-9f); EXPECT_FLOAT_EQ(arg.inputDelay(), 0.5e-9f); } TEST_F(ArcDcalcArgTest, SetInSlew) { ArcDcalcArg arg; arg.setInSlew(100e-12f); EXPECT_FLOAT_EQ(arg.inSlewFlt(), 100e-12f); } TEST_F(ArcDcalcArgTest, CopyConstruction) { ArcDcalcArg arg; arg.setLoadCap(2.0e-12f); arg.setInputDelay(1.0e-9f); arg.setInSlew(50e-12f); ArcDcalcArg copy(arg); EXPECT_FLOAT_EQ(copy.loadCap(), 2.0e-12f); EXPECT_FLOAT_EQ(copy.inputDelay(), 1.0e-9f); EXPECT_FLOAT_EQ(copy.inSlewFlt(), 50e-12f); EXPECT_EQ(copy.inPin(), nullptr); EXPECT_EQ(copy.drvrPin(), nullptr); } //////////////////////////////////////////////////////////////// class ArcDcalcResultTest : public ::testing::Test { protected: void SetUp() override { initDelayConstants(); } }; TEST_F(ArcDcalcResultTest, DefaultConstruction) { ArcDcalcResult result; EXPECT_FLOAT_EQ(delayAsFloat(result.gateDelay()), 0.0f); EXPECT_FLOAT_EQ(delayAsFloat(result.drvrSlew()), 0.0f); } TEST_F(ArcDcalcResultTest, SetGateDelay) { ArcDcalcResult result; result.setGateDelay(1.5e-10f); EXPECT_FLOAT_EQ(delayAsFloat(result.gateDelay()), 1.5e-10f); } TEST_F(ArcDcalcResultTest, SetDrvrSlew) { ArcDcalcResult result; result.setDrvrSlew(200e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.drvrSlew()), 200e-12f); } TEST_F(ArcDcalcResultTest, LoadDelaysAndSlews) { size_t load_count = 3; ArcDcalcResult result(load_count); result.setWireDelay(0, 10e-12f); result.setWireDelay(1, 20e-12f); result.setWireDelay(2, 30e-12f); result.setLoadSlew(0, 100e-12f); result.setLoadSlew(1, 110e-12f); result.setLoadSlew(2, 120e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(0)), 10e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(1)), 20e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(2)), 30e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(0)), 100e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(1)), 110e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(2)), 120e-12f); } TEST_F(ArcDcalcResultTest, SetLoadCount) { ArcDcalcResult result; result.setLoadCount(2); result.setWireDelay(0, 5e-12f); result.setWireDelay(1, 15e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(0)), 5e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(1)), 15e-12f); } TEST_F(ArcDcalcResultTest, ZeroLoadCount) { ArcDcalcResult result(0); result.setGateDelay(1.0e-9f); EXPECT_FLOAT_EQ(delayAsFloat(result.gateDelay()), 1.0e-9f); } //////////////////////////////////////////////////////////////// // Additional FindRoot coverage tests (tests the 4-arg overload more) class FindRootAdditionalTest : public ::testing::Test {}; // Test when y1 == 0 (exact root at x1) TEST_F(FindRootAdditionalTest, RootAtX1) { FindRootFunc func = [](double x, double &y, double &dy) { y = x - 5.0; dy = 1.0; }; bool fail = false; // y1 = 5-5 = 0, y2 = 10-5 = 5 double root = findRoot(func, 5.0, 0.0, 10.0, 5.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 5.0, 1e-8); } // Test when y2 == 0 (exact root at x2) TEST_F(FindRootAdditionalTest, RootAtX2) { FindRootFunc func = [](double x, double &y, double &dy) { y = x - 5.0; dy = 1.0; }; bool fail = false; // y1 = 0-5 = -5, y2 = 5-5 = 0 double root = findRoot(func, 0.0, -5.0, 5.0, 0.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 5.0, 1e-8); } // Test when both y values are positive (no root bracket) => fail TEST_F(FindRootAdditionalTest, BothPositiveFails) { FindRootFunc func = [](double x, double &y, double &dy) { y = x * x + 1.0; dy = 2.0 * x; }; bool fail = false; // y1 = 2, y2 = 5 -- both positive double root = findRoot(func, 1.0, 2.0, 2.0, 5.0, 1e-10, 100, fail); (void)root; EXPECT_TRUE(fail); } // Test when both y values are negative (no root bracket) => fail TEST_F(FindRootAdditionalTest, BothNegativeFails) { FindRootFunc func = [](double x, double &y, double &dy) { y = -x * x - 1.0; dy = -2.0 * x; }; bool fail = false; double root = findRoot(func, 1.0, -2.0, 2.0, -5.0, 1e-10, 100, fail); (void)root; EXPECT_TRUE(fail); } // Test max iterations exceeded (tight tolerance, few iterations) TEST_F(FindRootAdditionalTest, MaxIterationsExceeded) { FindRootFunc func = [](double x, double &y, double &dy) { y = x * x - 2.0; dy = 2.0 * x; }; bool fail = false; // Very tight tolerance with only 1 iteration double root = findRoot(func, 0.0, 3.0, 1e-15, 1, fail); (void)root; EXPECT_TRUE(fail); } // Test with y1 > 0 (swap happens internally) TEST_F(FindRootAdditionalTest, SwapWhenY1Positive) { FindRootFunc func = [](double x, double &y, double &dy) { y = x - 3.0; dy = 1.0; }; bool fail = false; // y1 = 2.0 > 0, y2 = -2.0 < 0 => swap internally double root = findRoot(func, 5.0, 2.0, 1.0, -2.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 3.0, 1e-8); } // Test cubic root TEST_F(FindRootAdditionalTest, CubicRoot) { FindRootFunc func = [](double x, double &y, double &dy) { y = x * x * x - 8.0; dy = 3.0 * x * x; }; bool fail = false; double root = findRoot(func, 1.0, 3.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 2.0, 1e-8); } // Test 2-arg findRoot (without pre-computed y values) TEST_F(FindRootAdditionalTest, TwoArgOverloadCubic) { FindRootFunc func = [](double x, double &y, double &dy) { y = x * x * x - 27.0; dy = 3.0 * x * x; }; bool fail = false; double root = findRoot(func, 2.0, 4.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 3.0, 1e-8); } //////////////////////////////////////////////////////////////// // ArcDcalcArg additional coverage TEST_F(ArcDcalcArgTest, SetParasitic) { ArcDcalcArg arg; EXPECT_EQ(arg.parasitic(), nullptr); // Set a dummy parasitic pointer (just testing the setter) int dummy = 42; arg.setParasitic(reinterpret_cast(&dummy)); EXPECT_NE(arg.parasitic(), nullptr); // Reset to null arg.setParasitic(nullptr); EXPECT_EQ(arg.parasitic(), nullptr); } TEST_F(ArcDcalcArgTest, FullConstructor) { // Test the 7-arg constructor with all nulls for pointers ArcDcalcArg arg(nullptr, nullptr, nullptr, nullptr, 1.5e-10f, 2.0e-12f, nullptr); EXPECT_EQ(arg.inPin(), nullptr); EXPECT_EQ(arg.drvrPin(), nullptr); EXPECT_EQ(arg.edge(), nullptr); EXPECT_EQ(arg.arc(), nullptr); EXPECT_FLOAT_EQ(arg.inSlewFlt(), 1.5e-10f); EXPECT_FLOAT_EQ(arg.loadCap(), 2.0e-12f); EXPECT_EQ(arg.parasitic(), nullptr); EXPECT_FLOAT_EQ(arg.inputDelay(), 0.0f); } TEST_F(ArcDcalcArgTest, InputDelayConstructor) { // Test the 5-arg constructor with input_delay ArcDcalcArg arg(nullptr, nullptr, nullptr, nullptr, 3.0e-9f); EXPECT_FLOAT_EQ(arg.inputDelay(), 3.0e-9f); EXPECT_FLOAT_EQ(arg.loadCap(), 0.0f); EXPECT_FLOAT_EQ(arg.inSlewFlt(), 0.0f); EXPECT_EQ(arg.parasitic(), nullptr); } //////////////////////////////////////////////////////////////// // ArcDcalcResult additional coverage TEST_F(ArcDcalcResultTest, MultipleLoadResizes) { ArcDcalcResult result; // First set some loads result.setLoadCount(3); result.setWireDelay(0, 1e-12f); result.setWireDelay(1, 2e-12f); result.setWireDelay(2, 3e-12f); result.setLoadSlew(0, 10e-12f); result.setLoadSlew(1, 20e-12f); result.setLoadSlew(2, 30e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(0)), 1e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(2)), 3e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(1)), 20e-12f); // Resize larger result.setLoadCount(5); result.setWireDelay(3, 4e-12f); result.setWireDelay(4, 5e-12f); result.setLoadSlew(3, 40e-12f); result.setLoadSlew(4, 50e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(3)), 4e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(4)), 5e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(4)), 50e-12f); } TEST_F(ArcDcalcResultTest, SingleLoad) { ArcDcalcResult result(1); result.setGateDelay(5e-10f); result.setDrvrSlew(1e-10f); result.setWireDelay(0, 2e-12f); result.setLoadSlew(0, 1.1e-10f); EXPECT_FLOAT_EQ(delayAsFloat(result.gateDelay()), 5e-10f); EXPECT_FLOAT_EQ(delayAsFloat(result.drvrSlew()), 1e-10f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(0)), 2e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(0)), 1.1e-10f); } TEST_F(ArcDcalcResultTest, LargeLoadCount) { size_t count = 100; ArcDcalcResult result(count); for (size_t i = 0; i < count; i++) { result.setWireDelay(i, static_cast(i) * 1e-12f); result.setLoadSlew(i, static_cast(i) * 10e-12f); } for (size_t i = 0; i < count; i++) { EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(i)), static_cast(i) * 1e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(i)), static_cast(i) * 10e-12f); } } TEST_F(ArcDcalcResultTest, OverwriteValues) { ArcDcalcResult result(2); result.setGateDelay(1e-10f); result.setDrvrSlew(2e-10f); result.setWireDelay(0, 3e-12f); result.setLoadSlew(0, 4e-12f); // Overwrite all values result.setGateDelay(10e-10f); result.setDrvrSlew(20e-10f); result.setWireDelay(0, 30e-12f); result.setLoadSlew(0, 40e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.gateDelay()), 10e-10f); EXPECT_FLOAT_EQ(delayAsFloat(result.drvrSlew()), 20e-10f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(0)), 30e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(0)), 40e-12f); } //////////////////////////////////////////////////////////////// // DelayCalc registry additional tests TEST_F(DcalcRegistryTest, AllRegisteredNames) { StringSeq names = delayCalcNames(); // Verify all names are non-null and recognized for (const char *name : names) { EXPECT_NE(name, nullptr); EXPECT_TRUE(isDelayCalcName(name)); } } TEST_F(DcalcRegistryTest, MakeNonexistentReturnsNull) { // Non-existent delay calc name should return nullptr ArcDelayCalc *calc = makeDelayCalc("does_not_exist_123", nullptr); EXPECT_EQ(calc, nullptr); } TEST_F(DcalcRegistryTest, VariousInvalidNames) { EXPECT_FALSE(isDelayCalcName("Unit")); // case sensitive EXPECT_FALSE(isDelayCalcName("LUMPED_CAP")); EXPECT_FALSE(isDelayCalcName("invalid_calc")); EXPECT_FALSE(isDelayCalcName(" ")); EXPECT_FALSE(isDelayCalcName("unit ")); // trailing space } //////////////////////////////////////////////////////////////// // Sta-initialized tests for delay calculator subclasses // These tests instantiate actual delay calculators through the // registry and exercise their virtual methods. } // namespace sta #include #include "Sta.hh" #include "ReportTcl.hh" #include "Corner.hh" #include "dcalc/UnitDelayCalc.hh" #include "dcalc/DmpDelayCalc.hh" #include "dcalc/DmpCeff.hh" #include "dcalc/CcsCeffDelayCalc.hh" #include "dcalc/PrimaDelayCalc.hh" #include "dcalc/LumpedCapDelayCalc.hh" #include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" #include "Units.hh" #include "MinMax.hh" #include "Transition.hh" #include "dcalc/NetCaps.hh" namespace sta { class StaDcalcTest : public ::testing::Test { protected: void SetUp() override { interp_ = Tcl_CreateInterp(); initSta(); sta_ = new Sta; Sta::setSta(sta_); sta_->makeComponents(); ReportTcl *report = dynamic_cast(sta_->report()); if (report) report->setTclInterp(interp_); registerDelayCalcs(); } void TearDown() override { deleteDelayCalcs(); deleteAllMemory(); sta_ = nullptr; if (interp_) Tcl_DeleteInterp(interp_); interp_ = nullptr; } Sta *sta_; Tcl_Interp *interp_; }; // Test UnitDelayCalc instantiation and name TEST_F(StaDcalcTest, UnitDelayCalcName) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); EXPECT_STREQ(calc->name(), "unit"); delete calc; } // Test UnitDelayCalc::copy TEST_F(StaDcalcTest, UnitDelayCalcCopy) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); ArcDelayCalc *copy = calc->copy(); ASSERT_NE(copy, nullptr); EXPECT_STREQ(copy->name(), "unit"); delete copy; delete calc; } // Test UnitDelayCalc::reduceSupported TEST_F(StaDcalcTest, UnitDelayCalcReduceSupported) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); EXPECT_FALSE(calc->reduceSupported()); delete calc; } // Test UnitDelayCalc::findParasitic returns nullptr TEST_F(StaDcalcTest, UnitDelayCalcFindParasitic) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); Parasitic *p = calc->findParasitic(nullptr, nullptr, nullptr); EXPECT_EQ(p, nullptr); delete calc; } // Test UnitDelayCalc::reduceParasitic returns nullptr (Pin* overload) TEST_F(StaDcalcTest, UnitDelayCalcReduceParasitic) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); Parasitic *p = calc->reduceParasitic(static_cast(nullptr), static_cast(nullptr), static_cast(nullptr), static_cast(nullptr)); EXPECT_EQ(p, nullptr); delete calc; } // Test UnitDelayCalc::reduceParasitic (Net* overload) - void TEST_F(StaDcalcTest, UnitDelayCalcReduceParasiticNet) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); // Should not crash calc->reduceParasitic(static_cast(nullptr), static_cast(nullptr), static_cast(nullptr), static_cast(nullptr)); delete calc; } // Test UnitDelayCalc::setDcalcArgParasiticSlew (single) TEST_F(StaDcalcTest, UnitDelayCalcSetDcalcArgParasiticSlewSingle) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArg arg; calc->setDcalcArgParasiticSlew(arg, nullptr); delete calc; } // Test UnitDelayCalc::setDcalcArgParasiticSlew (seq) TEST_F(StaDcalcTest, UnitDelayCalcSetDcalcArgParasiticSlewSeq) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } // Test UnitDelayCalc::inputPortDelay TEST_F(StaDcalcTest, UnitDelayCalcInputPortDelay) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->inputPortDelay(nullptr, 0.0, nullptr, nullptr, load_pin_index_map, nullptr); // With empty load_pin_index_map, gate delay should be set EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } // Test UnitDelayCalc::gateDelay TEST_F(StaDcalcTest, UnitDelayCalcGateDelay) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->gateDelay(nullptr, nullptr, 0.0, 0.0, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } // Test UnitDelayCalc::gateDelays TEST_F(StaDcalcTest, UnitDelayCalcGateDelays) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; args.push_back(ArcDcalcArg()); args.push_back(ArcDcalcArg()); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResultSeq results = calc->gateDelays(args, load_pin_index_map, nullptr); EXPECT_EQ(results.size(), 2u); delete calc; } // Test UnitDelayCalc::checkDelay TEST_F(StaDcalcTest, UnitDelayCalcCheckDelay) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); ArcDelay delay = calc->checkDelay(nullptr, nullptr, 0.0, 0.0, 0.0, nullptr); EXPECT_GT(delayAsFloat(delay), 0.0f); delete calc; } // Test UnitDelayCalc::reportGateDelay TEST_F(StaDcalcTest, UnitDelayCalcReportGateDelay) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); std::string report = calc->reportGateDelay(nullptr, nullptr, 0.0, 0.0, nullptr, load_pin_index_map, nullptr, 3); EXPECT_FALSE(report.empty()); EXPECT_NE(report.find("Delay"), std::string::npos); delete calc; } // Test UnitDelayCalc::reportCheckDelay TEST_F(StaDcalcTest, UnitDelayCalcReportCheckDelay) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); std::string report = calc->reportCheckDelay(nullptr, nullptr, 0.0, nullptr, 0.0, 0.0, nullptr, 3); EXPECT_FALSE(report.empty()); EXPECT_NE(report.find("Check"), std::string::npos); delete calc; } // Test UnitDelayCalc::finishDrvrPin TEST_F(StaDcalcTest, UnitDelayCalcFinishDrvrPin) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); // Should not crash delete calc; } // Test lumped_cap name TEST_F(StaDcalcTest, LumpedCapDelayCalcName) { ArcDelayCalc *calc = makeDelayCalc("lumped_cap", sta_); ASSERT_NE(calc, nullptr); EXPECT_STREQ(calc->name(), "lumped_cap"); delete calc; } // Test lumped_cap copy TEST_F(StaDcalcTest, LumpedCapDelayCalcCopy) { ArcDelayCalc *calc = makeDelayCalc("lumped_cap", sta_); ASSERT_NE(calc, nullptr); ArcDelayCalc *copy = calc->copy(); ASSERT_NE(copy, nullptr); EXPECT_STREQ(copy->name(), "lumped_cap"); delete copy; delete calc; } // Test lumped_cap::reduceSupported TEST_F(StaDcalcTest, LumpedCapReduceSupported) { ArcDelayCalc *calc = makeDelayCalc("lumped_cap", sta_); ASSERT_NE(calc, nullptr); EXPECT_TRUE(calc->reduceSupported()); delete calc; } // Test dmp_ceff_elmore name TEST_F(StaDcalcTest, DmpCeffElmoreName) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_elmore", sta_); ASSERT_NE(calc, nullptr); EXPECT_STREQ(calc->name(), "dmp_ceff_elmore"); delete calc; } // Test dmp_ceff_elmore copy TEST_F(StaDcalcTest, DmpCeffElmoreCopy) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_elmore", sta_); ASSERT_NE(calc, nullptr); ArcDelayCalc *copy = calc->copy(); ASSERT_NE(copy, nullptr); EXPECT_STREQ(copy->name(), "dmp_ceff_elmore"); delete copy; delete calc; } // Test dmp_ceff_elmore::reduceSupported TEST_F(StaDcalcTest, DmpCeffElmoreReduceSupported) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_elmore", sta_); ASSERT_NE(calc, nullptr); EXPECT_TRUE(calc->reduceSupported()); delete calc; } // Test dmp_ceff_two_pole name TEST_F(StaDcalcTest, DmpCeffTwoPoleName) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_two_pole", sta_); ASSERT_NE(calc, nullptr); EXPECT_STREQ(calc->name(), "dmp_ceff_two_pole"); delete calc; } // Test dmp_ceff_two_pole copy TEST_F(StaDcalcTest, DmpCeffTwoPoleCopy) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_two_pole", sta_); ASSERT_NE(calc, nullptr); ArcDelayCalc *copy = calc->copy(); ASSERT_NE(copy, nullptr); EXPECT_STREQ(copy->name(), "dmp_ceff_two_pole"); delete copy; delete calc; } // Test dmp_ceff_two_pole::reduceSupported TEST_F(StaDcalcTest, DmpCeffTwoPoleReduceSupported) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_two_pole", sta_); ASSERT_NE(calc, nullptr); EXPECT_TRUE(calc->reduceSupported()); delete calc; } // Test arnoldi name TEST_F(StaDcalcTest, ArnoldiName) { ArcDelayCalc *calc = makeDelayCalc("arnoldi", sta_); ASSERT_NE(calc, nullptr); EXPECT_STREQ(calc->name(), "arnoldi"); delete calc; } // Test arnoldi copy TEST_F(StaDcalcTest, ArnoldiCopy) { ArcDelayCalc *calc = makeDelayCalc("arnoldi", sta_); ASSERT_NE(calc, nullptr); ArcDelayCalc *copy = calc->copy(); ASSERT_NE(copy, nullptr); EXPECT_STREQ(copy->name(), "arnoldi"); delete copy; delete calc; } // Test ccs_ceff name TEST_F(StaDcalcTest, CcsCeffName) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); EXPECT_STREQ(calc->name(), "ccs_ceff"); delete calc; } // Test ccs_ceff copy TEST_F(StaDcalcTest, CcsCeffCopy) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); ArcDelayCalc *copy = calc->copy(); ASSERT_NE(copy, nullptr); EXPECT_STREQ(copy->name(), "ccs_ceff"); delete copy; delete calc; } // Test ccs_ceff::reduceSupported TEST_F(StaDcalcTest, CcsCeffReduceSupported) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); EXPECT_TRUE(calc->reduceSupported()); delete calc; } // Test prima name TEST_F(StaDcalcTest, PrimaName) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); EXPECT_STREQ(calc->name(), "prima"); delete calc; } // Test prima copy TEST_F(StaDcalcTest, PrimaCopy) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); ArcDelayCalc *copy = calc->copy(); ASSERT_NE(copy, nullptr); EXPECT_STREQ(copy->name(), "prima"); delete copy; delete calc; } // Test prima::reduceSupported TEST_F(StaDcalcTest, PrimaReduceSupported) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); EXPECT_FALSE(calc->reduceSupported()); delete calc; } // Test prima::reduceParasitic returns nullptr TEST_F(StaDcalcTest, PrimaReduceParasitic) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); Parasitic *p = calc->reduceParasitic(static_cast(nullptr), static_cast(nullptr), static_cast(nullptr), static_cast(nullptr)); EXPECT_EQ(p, nullptr); delete calc; } // Test prima::finishDrvrPin TEST_F(StaDcalcTest, PrimaFinishDrvrPin) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); // Should not crash delete calc; } // Test all calcs can be instantiated and destroyed TEST_F(StaDcalcTest, AllCalcsInstantiateDestroy) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr) << "Failed to create: " << name; EXPECT_STREQ(calc->name(), name); delete calc; } } // Test all calcs copy and destroy TEST_F(StaDcalcTest, AllCalcsCopyDestroy) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr); ArcDelayCalc *copy = calc->copy(); ASSERT_NE(copy, nullptr); EXPECT_STREQ(copy->name(), name); delete copy; delete calc; } } // Test UnitDelayCalc with non-empty load_pin_index_map TEST_F(StaDcalcTest, UnitDelayCalcGateDelayWithLoads) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); // Use dummy pin pointers for the index map int dummy1 = 1, dummy2 = 2; const Pin *pin1 = reinterpret_cast(&dummy1); const Pin *pin2 = reinterpret_cast(&dummy2); load_pin_index_map[pin1] = 0; load_pin_index_map[pin2] = 1; ArcDcalcResult result = calc->gateDelay(nullptr, nullptr, 0.0, 0.0, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(0)), 0.0f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(1)), 0.0f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(0)), 0.0f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(1)), 0.0f); delete calc; } // Test UnitDelayCalc gateDelays with loads TEST_F(StaDcalcTest, UnitDelayCalcGateDelaysWithLoads) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; args.push_back(ArcDcalcArg()); LoadPinIndexMap load_pin_index_map(sta_->network()); int dummy1 = 1; const Pin *pin1 = reinterpret_cast(&dummy1); load_pin_index_map[pin1] = 0; ArcDcalcResultSeq results = calc->gateDelays(args, load_pin_index_map, nullptr); EXPECT_EQ(results.size(), 1u); EXPECT_GE(delayAsFloat(results[0].gateDelay()), 0.0f); EXPECT_FLOAT_EQ(delayAsFloat(results[0].wireDelay(0)), 0.0f); delete calc; } // Test UnitDelayCalc inputPortDelay with loads TEST_F(StaDcalcTest, UnitDelayCalcInputPortDelayWithLoads) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); int dummy1 = 1; const Pin *pin1 = reinterpret_cast(&dummy1); load_pin_index_map[pin1] = 0; ArcDcalcResult result = calc->inputPortDelay(nullptr, 1e-10, nullptr, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } // Test deprecated gateDelay interface on UnitDelayCalc TEST_F(StaDcalcTest, UnitDelayCalcDeprecatedGateDelay) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); ArcDelay gate_delay; Slew drvr_slew; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" calc->gateDelay(nullptr, 0.0, 0.0, nullptr, 0.0, nullptr, nullptr, gate_delay, drvr_slew); #pragma GCC diagnostic pop EXPECT_GE(delayAsFloat(gate_delay), 0.0f); delete calc; } // Test lumped_cap finishDrvrPin TEST_F(StaDcalcTest, LumpedCapFinishDrvrPin) { ArcDelayCalc *calc = makeDelayCalc("lumped_cap", sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); delete calc; } // Test dmp_ceff_elmore finishDrvrPin TEST_F(StaDcalcTest, DmpCeffElmoreFinishDrvrPin) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_elmore", sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); delete calc; } // Test dmp_ceff_two_pole finishDrvrPin TEST_F(StaDcalcTest, DmpCeffTwoPoleFinishDrvrPin) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_two_pole", sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); delete calc; } // Test ccs_ceff finishDrvrPin TEST_F(StaDcalcTest, CcsCeffFinishDrvrPin) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); delete calc; } // Test arnoldi reduceSupported TEST_F(StaDcalcTest, ArnoldiReduceSupported) { ArcDelayCalc *calc = makeDelayCalc("arnoldi", sta_); ASSERT_NE(calc, nullptr); EXPECT_TRUE(calc->reduceSupported()); delete calc; } // Test arnoldi finishDrvrPin TEST_F(StaDcalcTest, ArnoldiFinishDrvrPin) { ArcDelayCalc *calc = makeDelayCalc("arnoldi", sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); delete calc; } // Test NetCaps 4-arg constructor TEST_F(StaDcalcTest, NetCapsConstructor) { NetCaps caps(1.5e-12f, 2.0e-13f, 4.0f, true); EXPECT_FLOAT_EQ(caps.pinCap(), 1.5e-12f); EXPECT_FLOAT_EQ(caps.wireCap(), 2.0e-13f); EXPECT_FLOAT_EQ(caps.fanout(), 4.0f); EXPECT_TRUE(caps.hasNetLoad()); } // Test NetCaps default constructor and init TEST_F(StaDcalcTest, NetCapsDefaultAndInit) { NetCaps caps; caps.init(3e-12f, 1e-12f, 2.0f, false); EXPECT_FLOAT_EQ(caps.pinCap(), 3e-12f); EXPECT_FLOAT_EQ(caps.wireCap(), 1e-12f); EXPECT_FLOAT_EQ(caps.fanout(), 2.0f); EXPECT_FALSE(caps.hasNetLoad()); } // Test CcsCeffDelayCalc watchPin/clearWatchPins/watchPins TEST_F(StaDcalcTest, CcsCeffWatchPins) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); CcsCeffDelayCalc *ccs = dynamic_cast(calc); ASSERT_NE(ccs, nullptr); // Initially no watch pins PinSeq pins = ccs->watchPins(); EXPECT_TRUE(pins.empty()); // Add a watch pin int d1 = 1; const Pin *pin1 = reinterpret_cast(&d1); ccs->watchPin(pin1); pins = ccs->watchPins(); EXPECT_EQ(pins.size(), 1u); // Clear watch pins ccs->clearWatchPins(); pins = ccs->watchPins(); EXPECT_TRUE(pins.empty()); delete calc; } // Test PrimaDelayCalc watchPin/clearWatchPins/watchPins TEST_F(StaDcalcTest, PrimaWatchPins) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); PrimaDelayCalc *prima = dynamic_cast(calc); ASSERT_NE(prima, nullptr); // Initially no watch pins PinSeq pins = prima->watchPins(); EXPECT_TRUE(pins.empty()); // Add watch pins int d1 = 1; const Pin *pin1 = reinterpret_cast(&d1); prima->watchPin(pin1); pins = prima->watchPins(); EXPECT_EQ(pins.size(), 1u); // Clear prima->clearWatchPins(); pins = prima->watchPins(); EXPECT_TRUE(pins.empty()); delete calc; } // Test lumped_cap inputPortDelay TEST_F(StaDcalcTest, LumpedCapInputPortDelay) { ArcDelayCalc *calc = makeDelayCalc("lumped_cap", sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->inputPortDelay(nullptr, 0.0, nullptr, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } // Test lumped_cap setDcalcArgParasiticSlew single (null pin early exit) TEST_F(StaDcalcTest, LumpedCapSetDcalcArgParasiticSlewSingle) { ArcDelayCalc *calc = makeDelayCalc("lumped_cap", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArg arg; // null drvr_pin => early return calc->setDcalcArgParasiticSlew(arg, nullptr); delete calc; } // Test lumped_cap setDcalcArgParasiticSlew seq TEST_F(StaDcalcTest, LumpedCapSetDcalcArgParasiticSlewSeq) { ArcDelayCalc *calc = makeDelayCalc("lumped_cap", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; args.push_back(ArcDcalcArg()); // null drvr_pin calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } // Test dmp_ceff_elmore setDcalcArgParasiticSlew TEST_F(StaDcalcTest, DmpCeffElmoreSetDcalcArgSingle) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_elmore", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArg arg; calc->setDcalcArgParasiticSlew(arg, nullptr); delete calc; } // Test dmp_ceff_two_pole setDcalcArgParasiticSlew TEST_F(StaDcalcTest, DmpCeffTwoPoleSetDcalcArgSingle) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_two_pole", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArg arg; calc->setDcalcArgParasiticSlew(arg, nullptr); delete calc; } // Test ccs_ceff setDcalcArgParasiticSlew TEST_F(StaDcalcTest, CcsCeffSetDcalcArgSingle) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArg arg; calc->setDcalcArgParasiticSlew(arg, nullptr); delete calc; } // Test prima setDcalcArgParasiticSlew TEST_F(StaDcalcTest, PrimaSetDcalcArgSingle) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArg arg; calc->setDcalcArgParasiticSlew(arg, nullptr); delete calc; } // Test arnoldi setDcalcArgParasiticSlew TEST_F(StaDcalcTest, ArnoldiSetDcalcArgSingle) { ArcDelayCalc *calc = makeDelayCalc("arnoldi", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArg arg; calc->setDcalcArgParasiticSlew(arg, nullptr); delete calc; } // Test dmp_ceff_elmore inputPortDelay TEST_F(StaDcalcTest, DmpCeffElmoreInputPortDelay) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_elmore", sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->inputPortDelay(nullptr, 0.0, nullptr, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } // Test prima inputPortDelay TEST_F(StaDcalcTest, PrimaInputPortDelay) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->inputPortDelay(nullptr, 0.0, nullptr, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } // Test UnitDelayCalc direct constructor (covers UnitDelayCalc::UnitDelayCalc) TEST_F(StaDcalcTest, UnitDelayCalcDirectConstruct) { UnitDelayCalc *unit = new UnitDelayCalc(sta_); ASSERT_NE(unit, nullptr); EXPECT_STREQ(unit->name(), "unit"); delete unit; } // Test DmpCeffDelayCalc D0 destructor through DmpCeffDelayCalc pointer TEST_F(StaDcalcTest, DmpCeffDelayCalcDeleteViaBasePtr) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_elmore", sta_); ASSERT_NE(calc, nullptr); // Cast to DmpCeffDelayCalc* (which is the parent of DmpCeffElmore) DmpCeffDelayCalc *dmp = dynamic_cast(calc); ASSERT_NE(dmp, nullptr); // Delete through DmpCeffDelayCalc* triggers the D0 destructor variant delete dmp; } // Test DmpCeffElmoreDelayCalc constructor via factory TEST_F(StaDcalcTest, DmpCeffElmoreDirectFactory) { ArcDelayCalc *calc = makeDmpCeffElmoreDelayCalc(sta_); ASSERT_NE(calc, nullptr); EXPECT_STREQ(calc->name(), "dmp_ceff_elmore"); delete calc; } // Test DmpCeffTwoPoleDelayCalc constructor via factory TEST_F(StaDcalcTest, DmpCeffTwoPoleDirectFactory) { ArcDelayCalc *calc = makeDmpCeffTwoPoleDelayCalc(sta_); ASSERT_NE(calc, nullptr); EXPECT_STREQ(calc->name(), "dmp_ceff_two_pole"); delete calc; } // Test GraphDelayCalc::incrementalDelayTolerance TEST_F(StaDcalcTest, GraphDelayCalcIncrementalTolerance) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); float tol = gdc->incrementalDelayTolerance(); EXPECT_GE(tol, 0.0f); // Set a new tolerance gdc->setIncrementalDelayTolerance(0.05f); EXPECT_FLOAT_EQ(gdc->incrementalDelayTolerance(), 0.05f); // Restore gdc->setIncrementalDelayTolerance(tol); } // Test MultiDrvrNet default construction and setDcalcDrvr TEST_F(StaDcalcTest, MultiDrvrNetConstruct) { MultiDrvrNet mdn; EXPECT_EQ(mdn.dcalcDrvr(), nullptr); EXPECT_TRUE(mdn.drvrs().empty()); } // Test MultiDrvrNet setDcalcDrvr TEST_F(StaDcalcTest, MultiDrvrNetSetDcalcDrvr) { MultiDrvrNet mdn; // Use a dummy vertex pointer int dummy = 42; Vertex *v = reinterpret_cast(&dummy); mdn.setDcalcDrvr(v); EXPECT_EQ(mdn.dcalcDrvr(), v); } // Test dmp_ceff_two_pole inputPortDelay TEST_F(StaDcalcTest, DmpCeffTwoPoleInputPortDelay) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_two_pole", sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->inputPortDelay(nullptr, 0.0, nullptr, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } // Test arnoldi inputPortDelay TEST_F(StaDcalcTest, ArnoldiInputPortDelay) { ArcDelayCalc *calc = makeDelayCalc("arnoldi", sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->inputPortDelay(nullptr, 0.0, nullptr, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } // Test ccs_ceff inputPortDelay TEST_F(StaDcalcTest, CcsCeffInputPortDelay) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->inputPortDelay(nullptr, 0.0, nullptr, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } // Note: findParasitic and reduceParasitic tests with null DcalcAnalysisPt // crash because the implementations dereference the pointer internally. // These functions require a fully loaded design to test properly. // Test lumped_cap setDcalcArgParasiticSlew with loads in the arg TEST_F(StaDcalcTest, LumpedCapSetDcalcArgParasiticSlewWithLoads) { ArcDelayCalc *calc = makeDelayCalc("lumped_cap", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; ArcDcalcArg arg1; ArcDcalcArg arg2; args.push_back(arg1); args.push_back(arg2); calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } // Test dmp_ceff_elmore setDcalcArgParasiticSlew seq TEST_F(StaDcalcTest, DmpCeffElmoreSetDcalcArgSeq) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_elmore", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; args.push_back(ArcDcalcArg()); calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } // Test dmp_ceff_two_pole setDcalcArgParasiticSlew seq TEST_F(StaDcalcTest, DmpCeffTwoPoleSetDcalcArgSeq) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_two_pole", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; args.push_back(ArcDcalcArg()); calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } // Test ccs_ceff setDcalcArgParasiticSlew seq TEST_F(StaDcalcTest, CcsCeffSetDcalcArgSeq) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; args.push_back(ArcDcalcArg()); calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } // Test prima setDcalcArgParasiticSlew seq TEST_F(StaDcalcTest, PrimaSetDcalcArgSeq) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; args.push_back(ArcDcalcArg()); calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } // Test arnoldi setDcalcArgParasiticSlew seq TEST_F(StaDcalcTest, ArnoldiSetDcalcArgSeq) { ArcDelayCalc *calc = makeDelayCalc("arnoldi", sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; args.push_back(ArcDcalcArg()); calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } // Test GraphDelayCalc observer set/clear TEST_F(StaDcalcTest, GraphDelayCalcObserver) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); // Set observer to nullptr - should not crash gdc->setObserver(nullptr); } // Test GraphDelayCalc clear TEST_F(StaDcalcTest, GraphDelayCalcClear) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); gdc->clear(); // should not crash } // Test NetCaps totalCap TEST_F(StaDcalcTest, NetCapsTotalCap) { NetCaps caps(1e-12f, 2e-12f, 3.0f, true); // Total cap should be pin + wire float total = caps.pinCap() + caps.wireCap(); EXPECT_FLOAT_EQ(total, 3e-12f); } // Test PrimaDelayCalc setPrimaReduceOrder TEST_F(StaDcalcTest, PrimaSetReduceOrder) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); PrimaDelayCalc *prima = dynamic_cast(calc); ASSERT_NE(prima, nullptr); prima->setPrimaReduceOrder(4); delete calc; } // Test PrimaDelayCalc copy constructor (via copy()) TEST_F(StaDcalcTest, PrimaCopyDeepState) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); PrimaDelayCalc *prima = dynamic_cast(calc); ASSERT_NE(prima, nullptr); prima->setPrimaReduceOrder(6); ArcDelayCalc *copy = prima->copy(); ASSERT_NE(copy, nullptr); EXPECT_STREQ(copy->name(), "prima"); delete copy; delete calc; } // Test ArcDcalcArg with non-null edge/arc-like pointers // to cover inEdge(), drvrVertex(), drvrNet() accessors // We'll use the 7-arg constructor and test the pointer getters TEST_F(StaDcalcTest, ArcDcalcArgPointerGetters) { int dummy_pin1 = 1, dummy_pin2 = 2; int dummy_edge = 3, dummy_arc = 4; const Pin *pin1 = reinterpret_cast(&dummy_pin1); const Pin *pin2 = reinterpret_cast(&dummy_pin2); Edge *edge = reinterpret_cast(&dummy_edge); const TimingArc *arc = reinterpret_cast(&dummy_arc); ArcDcalcArg arg(pin1, pin2, edge, arc, 1e-10f, 2e-12f, nullptr); EXPECT_EQ(arg.inPin(), pin1); EXPECT_EQ(arg.drvrPin(), pin2); EXPECT_EQ(arg.edge(), edge); EXPECT_EQ(arg.arc(), arc); EXPECT_FLOAT_EQ(arg.inSlewFlt(), 1e-10f); EXPECT_FLOAT_EQ(arg.loadCap(), 2e-12f); } // Test CcsCeffDelayCalc watchWaveform with non-watched pin returns empty TEST_F(StaDcalcTest, CcsCeffWatchWaveformEmpty) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); CcsCeffDelayCalc *ccs = dynamic_cast(calc); ASSERT_NE(ccs, nullptr); // watchWaveform on a pin that's not being watched int d1 = 1; const Pin *pin = reinterpret_cast(&d1); Waveform wf = ccs->watchWaveform(pin); // Should return an empty waveform (no axis) EXPECT_EQ(wf.axis1(), nullptr); delete calc; } // Test PrimaDelayCalc watchWaveform with non-watched pin TEST_F(StaDcalcTest, PrimaWatchWaveformEmpty) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); PrimaDelayCalc *prima = dynamic_cast(calc); ASSERT_NE(prima, nullptr); int d1 = 1; const Pin *pin = reinterpret_cast(&d1); // watchWaveform returns a Waveform - just verify it doesn't crash Waveform wf = prima->watchWaveform(pin); // PrimaDelayCalc may return a non-null axis even for unwatched pins (void)wf; delete calc; } // Test DmpCeffDelayCalc copyState TEST_F(StaDcalcTest, DmpCeffCopyState) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_elmore", sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); // should not crash delete calc; } // Test PrimaDelayCalc copyState TEST_F(StaDcalcTest, PrimaCopyState) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); // should not crash delete calc; } // Test CcsCeffDelayCalc copyState TEST_F(StaDcalcTest, CcsCeffCopyState) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); // should not crash delete calc; } // Test GraphDelayCalc copyState TEST_F(StaDcalcTest, GraphDelayCalcCopyState) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); gdc->copyState(sta_); // should not crash } // Test GraphDelayCalc delaysInvalid TEST_F(StaDcalcTest, GraphDelayCalcDelaysInvalid) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); gdc->delaysInvalid(); // should not crash } // Test DelayCalc module-level functions TEST_F(StaDcalcTest, DelayCalcModuleFunctions) { // Test that isDelayCalcName works for all known names EXPECT_TRUE(isDelayCalcName("unit")); EXPECT_TRUE(isDelayCalcName("lumped_cap")); EXPECT_TRUE(isDelayCalcName("dmp_ceff_elmore")); EXPECT_TRUE(isDelayCalcName("dmp_ceff_two_pole")); EXPECT_TRUE(isDelayCalcName("arnoldi")); EXPECT_TRUE(isDelayCalcName("ccs_ceff")); EXPECT_TRUE(isDelayCalcName("prima")); } // Test NetCaps with zero values TEST_F(StaDcalcTest, NetCapsZero) { NetCaps caps(0.0f, 0.0f, 0.0f, false); EXPECT_FLOAT_EQ(caps.pinCap(), 0.0f); EXPECT_FLOAT_EQ(caps.wireCap(), 0.0f); EXPECT_FLOAT_EQ(caps.fanout(), 0.0f); EXPECT_FALSE(caps.hasNetLoad()); } // Test NetCaps init with different values TEST_F(StaDcalcTest, NetCapsInitMultiple) { NetCaps caps; caps.init(1e-12f, 2e-12f, 4.0f, true); EXPECT_FLOAT_EQ(caps.pinCap(), 1e-12f); EXPECT_FLOAT_EQ(caps.wireCap(), 2e-12f); EXPECT_FLOAT_EQ(caps.fanout(), 4.0f); EXPECT_TRUE(caps.hasNetLoad()); // Reinitialize with different values caps.init(5e-12f, 6e-12f, 8.0f, false); EXPECT_FLOAT_EQ(caps.pinCap(), 5e-12f); EXPECT_FLOAT_EQ(caps.wireCap(), 6e-12f); EXPECT_FLOAT_EQ(caps.fanout(), 8.0f); EXPECT_FALSE(caps.hasNetLoad()); } //////////////////////////////////////////////////////////////// // R5_ tests for additional dcalc coverage //////////////////////////////////////////////////////////////// // Test ArcDcalcResult copy TEST_F(ArcDcalcResultTest, CopyResult) { ArcDcalcResult result(2); result.setGateDelay(1e-10f); result.setDrvrSlew(2e-10f); result.setWireDelay(0, 3e-12f); result.setWireDelay(1, 4e-12f); result.setLoadSlew(0, 5e-12f); result.setLoadSlew(1, 6e-12f); ArcDcalcResult copy(result); EXPECT_FLOAT_EQ(delayAsFloat(copy.gateDelay()), 1e-10f); EXPECT_FLOAT_EQ(delayAsFloat(copy.drvrSlew()), 2e-10f); EXPECT_FLOAT_EQ(delayAsFloat(copy.wireDelay(0)), 3e-12f); EXPECT_FLOAT_EQ(delayAsFloat(copy.wireDelay(1)), 4e-12f); EXPECT_FLOAT_EQ(delayAsFloat(copy.loadSlew(0)), 5e-12f); EXPECT_FLOAT_EQ(delayAsFloat(copy.loadSlew(1)), 6e-12f); } // Test ArcDcalcArg assignment TEST_F(ArcDcalcArgTest, Assignment) { ArcDcalcArg arg; arg.setLoadCap(3.5e-12f); arg.setInputDelay(1.5e-9f); arg.setInSlew(200e-12f); ArcDcalcArg other; other = arg; EXPECT_FLOAT_EQ(other.loadCap(), 3.5e-12f); EXPECT_FLOAT_EQ(other.inputDelay(), 1.5e-9f); EXPECT_FLOAT_EQ(other.inSlewFlt(), 200e-12f); } // Test ArcDcalcArg: set and get all fields TEST_F(ArcDcalcArgTest, AllSettersGetters) { ArcDcalcArg arg; arg.setLoadCap(1e-12f); arg.setInputDelay(2e-9f); arg.setInSlew(3e-10f); int dummy = 0; arg.setParasitic(reinterpret_cast(&dummy)); EXPECT_FLOAT_EQ(arg.loadCap(), 1e-12f); EXPECT_FLOAT_EQ(arg.inputDelay(), 2e-9f); EXPECT_FLOAT_EQ(arg.inSlewFlt(), 3e-10f); EXPECT_NE(arg.parasitic(), nullptr); } // Test FindRoot: with derivative zero (should still converge or fail gracefully) TEST_F(FindRootAdditionalTest, FlatDerivative) { // Function with zero derivative at some points FindRootFunc func = [](double x, double &y, double &dy) { y = (x - 2.0) * (x - 2.0) * (x - 2.0); dy = 3.0 * (x - 2.0) * (x - 2.0); }; bool fail = false; // y at x=1 = -1, y at x=3 = 1 double root = findRoot(func, 1.0, 3.0, 1e-8, 100, fail); if (!fail) { EXPECT_NEAR(root, 2.0, 1e-4); } } // Test FindRoot: linear function TEST_F(FindRootAdditionalTest, LinearFunction) { FindRootFunc func = [](double x, double &y, double &dy) { y = 2.0 * x - 6.0; dy = 2.0; }; bool fail = false; double root = findRoot(func, 0.0, 10.0, 1e-12, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 3.0, 1e-8); } // Test FindRoot 4-arg: negative y1 and positive y2 TEST_F(FindRootAdditionalTest, FourArgNormalBracket) { FindRootFunc func = [](double x, double &y, double &dy) { y = x * x - 4.0; dy = 2.0 * x; }; bool fail = false; // y1 = 1-4 = -3, y2 = 9-4 = 5 double root = findRoot(func, 1.0, -3.0, 3.0, 5.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 2.0, 1e-8); } // Test ArcDcalcResult: default gate delay is zero TEST_F(ArcDcalcResultTest, DefaultValues) { ArcDcalcResult result; EXPECT_FLOAT_EQ(delayAsFloat(result.gateDelay()), 0.0f); EXPECT_FLOAT_EQ(delayAsFloat(result.drvrSlew()), 0.0f); } // Test UnitDelayCalc copyState TEST_F(StaDcalcTest, UnitDelayCalcCopyState) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); delete calc; } // Test LumpedCap copyState TEST_F(StaDcalcTest, LumpedCapCopyState) { ArcDelayCalc *calc = makeDelayCalc("lumped_cap", sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); delete calc; } // Test Arnoldi copyState TEST_F(StaDcalcTest, ArnoldiCopyState) { ArcDelayCalc *calc = makeDelayCalc("arnoldi", sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); delete calc; } // Test all calcs reduceSupported TEST_F(StaDcalcTest, AllCalcsReduceSupported) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr); // Just call reduceSupported, don't check value bool supported = calc->reduceSupported(); (void)supported; delete calc; } } // Test NetCaps with large values TEST_F(StaDcalcTest, NetCapsLargeValues) { NetCaps caps(100e-12f, 200e-12f, 1000.0f, true); EXPECT_FLOAT_EQ(caps.pinCap(), 100e-12f); EXPECT_FLOAT_EQ(caps.wireCap(), 200e-12f); EXPECT_FLOAT_EQ(caps.fanout(), 1000.0f); EXPECT_TRUE(caps.hasNetLoad()); } // Test ArcDcalcResult with resize down TEST_F(ArcDcalcResultTest, ResizeDown) { ArcDcalcResult result(5); for (size_t i = 0; i < 5; i++) { result.setWireDelay(i, static_cast(i) * 1e-12f); result.setLoadSlew(i, static_cast(i) * 10e-12f); } result.setLoadCount(2); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(0)), 0.0f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(1)), 1e-12f); } // Test MultiDrvrNet drvrs TEST_F(StaDcalcTest, MultiDrvrNetDrvrs) { MultiDrvrNet mdn; VertexSeq &drvrs = mdn.drvrs(); EXPECT_TRUE(drvrs.empty()); } // Test GraphDelayCalc delayCalc returns non-null after init TEST_F(StaDcalcTest, GraphDelayCalcExists) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); EXPECT_NE(gdc, nullptr); } // Test UnitDelayCalc reduceParasitic Net overload TEST_F(StaDcalcTest, UnitDelayCalcReduceParasiticNetOverload) { ArcDelayCalc *calc = makeDelayCalc("unit", sta_); ASSERT_NE(calc, nullptr); calc->reduceParasitic(static_cast(nullptr), static_cast(nullptr), static_cast(nullptr), static_cast(nullptr)); delete calc; } } // namespace sta //////////////////////////////////////////////////////////////// // Design-loading tests to exercise delay calculation paths // that require a fully loaded design with parasitics #include "Network.hh" #include "Graph.hh" #include "Sdc.hh" #include "Search.hh" #include "StaState.hh" #include "PortDirection.hh" #include "TimingRole.hh" #include "TimingArc.hh" #include "dcalc/ArnoldiDelayCalc.hh" namespace sta { // Test fixture that loads the ASAP7 reg1 design with SPEF class DesignDcalcTest : public ::testing::Test { protected: void SetUp() override { interp_ = Tcl_CreateInterp(); initSta(); sta_ = new Sta; Sta::setSta(sta_); sta_->makeComponents(); ReportTcl *report = dynamic_cast(sta_->report()); if (report) report->setTclInterp(interp_); registerDelayCalcs(); Corner *corner = sta_->cmdCorner(); const MinMaxAll *min_max = MinMaxAll::all(); LibertyLibrary *lib = sta_->readLiberty( "test/asap7/asap7sc7p5t_SEQ_RVT_FF_nldm_220123.lib", corner, min_max, false); ASSERT_NE(lib, nullptr); lib = sta_->readLiberty( "test/asap7/asap7sc7p5t_INVBUF_RVT_FF_nldm_220122.lib.gz", corner, min_max, false); ASSERT_NE(lib, nullptr); lib = sta_->readLiberty( "test/asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz", corner, min_max, false); ASSERT_NE(lib, nullptr); lib = sta_->readLiberty( "test/asap7/asap7sc7p5t_OA_RVT_FF_nldm_211120.lib.gz", corner, min_max, false); ASSERT_NE(lib, nullptr); lib = sta_->readLiberty( "test/asap7/asap7sc7p5t_AO_RVT_FF_nldm_211120.lib.gz", corner, min_max, false); ASSERT_NE(lib, nullptr); bool ok = sta_->readVerilog("test/reg1_asap7.v"); ASSERT_TRUE(ok); ok = sta_->linkDesign("top", true); ASSERT_TRUE(ok); // Read SPEF with reduction (default) Instance *top = sta_->network()->topInstance(); ok = sta_->readSpef("test/reg1_asap7.spef", top, corner, min_max, false, false, 1.0f, true); ASSERT_TRUE(ok); // Create clock Network *network = sta_->network(); Pin *clk1 = network->findPin(top, "clk1"); Pin *clk2 = network->findPin(top, "clk2"); Pin *clk3 = network->findPin(top, "clk3"); ASSERT_NE(clk1, nullptr); PinSet *clk_pins = new PinSet(network); clk_pins->insert(clk1); clk_pins->insert(clk2); clk_pins->insert(clk3); FloatSeq *waveform = new FloatSeq; waveform->push_back(0.0f); waveform->push_back(250.0f); sta_->makeClock("clk", clk_pins, false, 500.0f, waveform, nullptr); design_loaded_ = true; } void TearDown() override { deleteDelayCalcs(); deleteAllMemory(); sta_ = nullptr; if (interp_) Tcl_DeleteInterp(interp_); interp_ = nullptr; } Sta *sta_; Tcl_Interp *interp_; bool design_loaded_ = false; }; // Test updateTiming with dmp_ceff_elmore (exercises GraphDelayCalc::findDelays, // findDriverArcDelays, initRootSlews, ArcDcalcArg accessors, DmpCeff internals) TEST_F(DesignDcalcTest, TimingDmpCeffElmore) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); // Verify timing ran without crash SUCCEED(); } // Test with dmp_ceff_two_pole calculator TEST_F(DesignDcalcTest, TimingDmpCeffTwoPole) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_two_pole"); sta_->updateTiming(true); SUCCEED(); } // Test with lumped_cap calculator TEST_F(DesignDcalcTest, TimingLumpedCap) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("lumped_cap"); sta_->updateTiming(true); SUCCEED(); } // Test with arnoldi calculator (exercises ArnoldiDelayCalc reduce) TEST_F(DesignDcalcTest, TimingArnoldi) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("arnoldi"); // Re-read SPEF without reduction so arnoldi can do its own reduction Corner *corner = sta_->cmdCorner(); Instance *top = sta_->network()->topInstance(); sta_->readSpef("test/reg1_asap7.spef", top, corner, MinMaxAll::all(), false, false, 1.0f, false); sta_->updateTiming(true); SUCCEED(); } // Test with unit calculator TEST_F(DesignDcalcTest, TimingUnit) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("unit"); sta_->updateTiming(true); SUCCEED(); } // Test GraphDelayCalc findDelays directly TEST_F(DesignDcalcTest, GraphDelayCalcFindDelays) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); // findDelays triggers the full delay calculation pipeline sta_->findDelays(); SUCCEED(); } // Test that findDelays exercises multiDrvrNet (through internal paths) TEST_F(DesignDcalcTest, GraphDelayCalcWithGraph) { ASSERT_TRUE(design_loaded_); GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); // After timing, verify graph has vertices with delays computed sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); // Verify the graph was built and delay calc ran Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); } // Test delay calculation with CCS/CEFF TEST_F(DesignDcalcTest, TimingCcsCeff) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("ccs_ceff"); sta_->updateTiming(true); SUCCEED(); } // Test prima delay calculator with design TEST_F(DesignDcalcTest, TimingPrima) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("prima"); // Re-read SPEF without reduction for prima Corner *corner = sta_->cmdCorner(); Instance *top = sta_->network()->topInstance(); sta_->readSpef("test/reg1_asap7.spef", top, corner, MinMaxAll::all(), false, false, 1.0f, false); sta_->updateTiming(true); SUCCEED(); } // Test incremental delay tolerance with actual delays TEST_F(DesignDcalcTest, IncrementalDelayWithDesign) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->setIncrementalDelayTolerance(0.001f); sta_->updateTiming(true); // Run again - should use incremental sta_->updateTiming(false); SUCCEED(); } // Test ArnoldiDelayCalc reduce with loaded parasitics TEST_F(DesignDcalcTest, ArnoldiReduceParasiticWithDesign) { ASSERT_TRUE(design_loaded_); // Read SPEF without reduction Corner *corner = sta_->cmdCorner(); Instance *top = sta_->network()->topInstance(); sta_->readSpef("test/reg1_asap7.spef", top, corner, MinMaxAll::all(), false, false, 1.0f, false); ArcDelayCalc *calc = makeDelayCalc("arnoldi", sta_); ASSERT_NE(calc, nullptr); Network *network = sta_->network(); Instance *u1 = network->findChild(top, "u1"); if (u1) { Pin *y_pin = network->findPin(u1, "Y"); if (y_pin) { const MinMax *mm = MinMax::max(); DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); const Net *net = network->net(y_pin); Parasitics *parasitics = sta_->parasitics(); if (net) { ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm); Parasitic *pnet = parasitics->findParasiticNetwork(net, ap); if (pnet) { // Arnoldi reduce (Pin* overload) Parasitic *reduced = calc->reduceParasitic(pnet, y_pin, RiseFall::rise(), dcalc_ap); // May or may not return a reduced model depending on network size (void)reduced; } } } } delete calc; } // Test switching delay calculator mid-flow TEST_F(DesignDcalcTest, SwitchDelayCalcMidFlow) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); sta_->setArcDelayCalc("lumped_cap"); sta_->updateTiming(true); sta_->setArcDelayCalc("dmp_ceff_two_pole"); sta_->updateTiming(true); SUCCEED(); } // Test delay calculation exercises ArcDcalcArg::inEdge/drvrVertex/drvrNet TEST_F(DesignDcalcTest, ArcDcalcArgAccessorsWithDesign) { ASSERT_TRUE(design_loaded_); // These accessors are exercised internally by the delay calculators // during findDelays. Just verify timing runs successfully. sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->findDelays(); // Verify we can query arrival times (which means delays were computed) Network *network = sta_->network(); Instance *top = network->topInstance(); Pin *out = network->findPin(top, "out"); if (out) { Graph *graph = sta_->graph(); if (graph) { Vertex *v = graph->pinLoadVertex(out); (void)v; // Just verify it doesn't crash } } } //////////////////////////////////////////////////////////////// // R6_ tests for additional dcalc coverage //////////////////////////////////////////////////////////////// // NetCaps: init with different values TEST_F(StaDcalcTest, NetCapsInitVariants) { NetCaps caps; caps.init(0.0f, 0.0f, 0.0f, false); EXPECT_FLOAT_EQ(caps.pinCap(), 0.0f); EXPECT_FLOAT_EQ(caps.wireCap(), 0.0f); EXPECT_FLOAT_EQ(caps.fanout(), 0.0f); EXPECT_FALSE(caps.hasNetLoad()); caps.init(1e-10f, 2e-10f, 8.0f, true); EXPECT_FLOAT_EQ(caps.pinCap(), 1e-10f); EXPECT_FLOAT_EQ(caps.wireCap(), 2e-10f); EXPECT_FLOAT_EQ(caps.fanout(), 8.0f); EXPECT_TRUE(caps.hasNetLoad()); } // NetCaps: parameterized constructor with zero values TEST_F(StaDcalcTest, NetCapsConstructorZero) { NetCaps caps(0.0f, 0.0f, 0.0f, false); EXPECT_FLOAT_EQ(caps.pinCap(), 0.0f); EXPECT_FLOAT_EQ(caps.wireCap(), 0.0f); EXPECT_FLOAT_EQ(caps.fanout(), 0.0f); EXPECT_FALSE(caps.hasNetLoad()); } // NetCaps: parameterized constructor with large values TEST_F(StaDcalcTest, NetCapsConstructorLarge) { NetCaps caps(1e-6f, 5e-7f, 100.0f, true); EXPECT_FLOAT_EQ(caps.pinCap(), 1e-6f); EXPECT_FLOAT_EQ(caps.wireCap(), 5e-7f); EXPECT_FLOAT_EQ(caps.fanout(), 100.0f); EXPECT_TRUE(caps.hasNetLoad()); } // ArcDcalcArg: drvrCell returns nullptr with null drvrPin TEST_F(ArcDcalcArgTest, DrvrCellNullPin) { ArcDcalcArg arg; // With null drvrPin, drvrCell returns nullptr // Can't call drvrCell with null arc, it would dereference arc_. // Skip - protected territory EXPECT_EQ(arg.drvrPin(), nullptr); } // ArcDcalcArg: assignment/move semantics via vector TEST_F(ArcDcalcArgTest, ArgInVector) { ArcDcalcArgSeq args; ArcDcalcArg arg1; arg1.setLoadCap(1.0e-12f); arg1.setInSlew(50e-12f); arg1.setInputDelay(1e-9f); args.push_back(arg1); ArcDcalcArg arg2; arg2.setLoadCap(2.0e-12f); arg2.setInSlew(100e-12f); arg2.setInputDelay(2e-9f); args.push_back(arg2); EXPECT_EQ(args.size(), 2u); EXPECT_FLOAT_EQ(args[0].loadCap(), 1.0e-12f); EXPECT_FLOAT_EQ(args[1].loadCap(), 2.0e-12f); EXPECT_FLOAT_EQ(args[0].inSlewFlt(), 50e-12f); EXPECT_FLOAT_EQ(args[1].inSlewFlt(), 100e-12f); } // ArcDcalcResult: copy semantics TEST_F(ArcDcalcResultTest, ResultCopy) { ArcDcalcResult result(3); result.setGateDelay(5e-10f); result.setDrvrSlew(2e-10f); result.setWireDelay(0, 1e-12f); result.setWireDelay(1, 2e-12f); result.setWireDelay(2, 3e-12f); result.setLoadSlew(0, 10e-12f); result.setLoadSlew(1, 20e-12f); result.setLoadSlew(2, 30e-12f); ArcDcalcResult copy = result; EXPECT_FLOAT_EQ(delayAsFloat(copy.gateDelay()), 5e-10f); EXPECT_FLOAT_EQ(delayAsFloat(copy.drvrSlew()), 2e-10f); EXPECT_FLOAT_EQ(delayAsFloat(copy.wireDelay(0)), 1e-12f); EXPECT_FLOAT_EQ(delayAsFloat(copy.wireDelay(2)), 3e-12f); EXPECT_FLOAT_EQ(delayAsFloat(copy.loadSlew(1)), 20e-12f); } // ArcDcalcResult: in vector (exercises copy/move) TEST_F(ArcDcalcResultTest, ResultInVector) { ArcDcalcResultSeq results; for (int i = 0; i < 5; i++) { ArcDcalcResult r(2); r.setGateDelay(static_cast(i) * 1e-10f); r.setDrvrSlew(static_cast(i) * 0.5e-10f); r.setWireDelay(0, static_cast(i) * 1e-12f); r.setWireDelay(1, static_cast(i) * 2e-12f); r.setLoadSlew(0, static_cast(i) * 5e-12f); r.setLoadSlew(1, static_cast(i) * 10e-12f); results.push_back(r); } EXPECT_EQ(results.size(), 5u); EXPECT_FLOAT_EQ(delayAsFloat(results[3].gateDelay()), 3e-10f); EXPECT_FLOAT_EQ(delayAsFloat(results[4].wireDelay(1)), 8e-12f); } // GraphDelayCalc: delaysInvalid TEST_F(StaDcalcTest, GraphDelayCalcDelaysInvalid2) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); // Should not crash gdc->delaysInvalid(); } // GraphDelayCalc: clear TEST_F(StaDcalcTest, GraphDelayCalcClear2) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); gdc->clear(); } // GraphDelayCalc: copyState TEST_F(StaDcalcTest, GraphDelayCalcCopyState2) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); gdc->copyState(sta_); } // Test all calcs: finishDrvrPin does not crash TEST_F(StaDcalcTest, AllCalcsFinishDrvrPin) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr) << "Failed for: " << name; calc->finishDrvrPin(); delete calc; } } // Test all calcs: setDcalcArgParasiticSlew (single) with empty arg TEST_F(StaDcalcTest, AllCalcsSetDcalcArgSingle) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr) << "Failed for: " << name; ArcDcalcArg arg; calc->setDcalcArgParasiticSlew(arg, nullptr); delete calc; } } // Test all calcs: setDcalcArgParasiticSlew (seq) with empty seq TEST_F(StaDcalcTest, AllCalcsSetDcalcArgSeqEmpty) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr) << "Failed for: " << name; ArcDcalcArgSeq args; calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } } // Test all calcs: inputPortDelay with null args TEST_F(StaDcalcTest, AllCalcsInputPortDelayNull) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr) << "Failed for: " << name; LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->inputPortDelay(nullptr, 0.0, nullptr, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f) << "Failed for: " << name; delete calc; } } // FindRoot: additional test for 4-arg overload with tight bounds TEST_F(FindRootAdditionalTest, TightBoundsLinear) { FindRootFunc func = [](double x, double &y, double &dy) { y = 2.0 * x - 6.0; dy = 2.0; }; bool fail = false; // y1 = 2*2.9-6 = -0.2, y2 = 2*3.1-6 = 0.2 double root = findRoot(func, 2.9, -0.2, 3.1, 0.2, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 3.0, 1e-8); } // FindRoot: test where Newton step goes out of bracket TEST_F(FindRootAdditionalTest, NewtonOutOfBracket) { // Using a function where Newton step may overshoot FindRootFunc func = [](double x, double &y, double &dy) { y = x * x * x - x - 2.0; dy = 3.0 * x * x - 1.0; }; bool fail = false; double root = findRoot(func, 1.0, 2.0, 1e-10, 100, fail); EXPECT_FALSE(fail); // Root is near 1.52138 EXPECT_NEAR(root, 1.52138, 1e-4); } // FindRoot: sin function TEST_F(FindRootAdditionalTest, SinRoot) { FindRootFunc func = [](double x, double &y, double &dy) { y = sin(x); dy = cos(x); }; bool fail = false; // Root near pi double root = findRoot(func, 3.0, 3.3, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, M_PI, 1e-8); } // FindRoot: exponential function TEST_F(FindRootAdditionalTest, ExpMinusConst) { FindRootFunc func = [](double x, double &y, double &dy) { y = exp(x) - 3.0; dy = exp(x); }; bool fail = false; double root = findRoot(func, 0.0, 2.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, log(3.0), 1e-8); } // GraphDelayCalc: levelsChangedBefore TEST_F(StaDcalcTest, GraphDelayCalcLevelsChangedBefore) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); gdc->levelsChangedBefore(); } // GraphDelayCalc: setObserver with nullptr TEST_F(StaDcalcTest, GraphDelayCalcSetObserverNull) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); gdc->setObserver(nullptr); } // MultiDrvrNet: drvrs vector TEST_F(StaDcalcTest, MultiDrvrNetDrvrs2) { MultiDrvrNet mdn; EXPECT_TRUE(mdn.drvrs().empty()); // drvrs() returns a reference to internal vector const auto &drvrs = mdn.drvrs(); EXPECT_EQ(drvrs.size(), 0u); } // ArcDcalcArg: multiple set/get cycles TEST_F(ArcDcalcArgTest, MultipleSetGetCycles) { ArcDcalcArg arg; for (int i = 0; i < 10; i++) { float cap = static_cast(i) * 1e-12f; float delay = static_cast(i) * 1e-9f; float slew = static_cast(i) * 50e-12f; arg.setLoadCap(cap); arg.setInputDelay(delay); arg.setInSlew(slew); EXPECT_FLOAT_EQ(arg.loadCap(), cap); EXPECT_FLOAT_EQ(arg.inputDelay(), delay); EXPECT_FLOAT_EQ(arg.inSlewFlt(), slew); } } // ArcDcalcResult: zero gate delay and nonzero wire delays TEST_F(ArcDcalcResultTest, ZeroGateNonzeroWire) { ArcDcalcResult result(2); result.setGateDelay(0.0f); result.setDrvrSlew(0.0f); result.setWireDelay(0, 5e-12f); result.setWireDelay(1, 10e-12f); result.setLoadSlew(0, 50e-12f); result.setLoadSlew(1, 100e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.gateDelay()), 0.0f); EXPECT_FLOAT_EQ(delayAsFloat(result.drvrSlew()), 0.0f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(0)), 5e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(1)), 10e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(0)), 50e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(1)), 100e-12f); } // ArcDcalcResult: resize down then up TEST_F(ArcDcalcResultTest, ResizeDownThenUp) { ArcDcalcResult result(5); for (size_t i = 0; i < 5; i++) { result.setWireDelay(i, static_cast(i) * 1e-12f); } result.setLoadCount(2); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(0)), 0.0f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(1)), 1e-12f); result.setLoadCount(4); result.setWireDelay(2, 22e-12f); result.setWireDelay(3, 33e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(2)), 22e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(3)), 33e-12f); } // DesignDcalc: timing with ccs_ceff calculator TEST_F(DesignDcalcTest, TimingCcsCeff2) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("ccs_ceff"); sta_->updateTiming(true); SUCCEED(); } // DesignDcalc: timing with prima calculator TEST_F(DesignDcalcTest, TimingPrima2) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("prima"); sta_->updateTiming(true); SUCCEED(); } // DesignDcalc: findDelays with lumped_cap TEST_F(DesignDcalcTest, FindDelaysLumpedCap) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("lumped_cap"); sta_->findDelays(); SUCCEED(); } // DesignDcalc: findDelays with unit TEST_F(DesignDcalcTest, FindDelaysUnit) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("unit"); sta_->findDelays(); SUCCEED(); } // DesignDcalc: findDelays with dmp_ceff_two_pole TEST_F(DesignDcalcTest, FindDelaysDmpTwoPole) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_two_pole"); sta_->findDelays(); SUCCEED(); } // DesignDcalc: findDelays with arnoldi TEST_F(DesignDcalcTest, FindDelaysArnoldi) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("arnoldi"); sta_->findDelays(); SUCCEED(); } // DesignDcalc: findDelays with ccs_ceff TEST_F(DesignDcalcTest, FindDelaysCcsCeff) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("ccs_ceff"); sta_->findDelays(); SUCCEED(); } // DesignDcalc: findDelays with prima TEST_F(DesignDcalcTest, FindDelaysPrima) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("prima"); sta_->findDelays(); SUCCEED(); } // ArcDcalcArg: copy constructor TEST_F(ArcDcalcArgTest, CopyConstructor) { ArcDcalcArg arg; arg.setLoadCap(5.0e-12f); arg.setInSlew(100e-12f); arg.setInputDelay(3e-9f); arg.setParasitic(nullptr); ArcDcalcArg copy(arg); EXPECT_FLOAT_EQ(copy.loadCap(), 5.0e-12f); EXPECT_FLOAT_EQ(copy.inSlewFlt(), 100e-12f); EXPECT_FLOAT_EQ(copy.inputDelay(), 3e-9f); EXPECT_EQ(copy.parasitic(), nullptr); EXPECT_EQ(copy.inPin(), nullptr); EXPECT_EQ(copy.drvrPin(), nullptr); EXPECT_EQ(copy.edge(), nullptr); EXPECT_EQ(copy.arc(), nullptr); } // ArcDcalcArg: default constructed values TEST_F(ArcDcalcArgTest, DefaultValues) { ArcDcalcArg arg; EXPECT_EQ(arg.inPin(), nullptr); EXPECT_EQ(arg.drvrPin(), nullptr); EXPECT_EQ(arg.edge(), nullptr); EXPECT_EQ(arg.arc(), nullptr); EXPECT_EQ(arg.parasitic(), nullptr); EXPECT_FLOAT_EQ(arg.loadCap(), 0.0f); EXPECT_FLOAT_EQ(arg.inputDelay(), 0.0f); } // ArcDcalcArg: setParasitic round-trip TEST_F(ArcDcalcArgTest, SetParasitic2) { ArcDcalcArg arg; EXPECT_EQ(arg.parasitic(), nullptr); // Set to a non-null sentinel (won't be dereferenced) const Parasitic *fake = reinterpret_cast(0x1234); arg.setParasitic(fake); EXPECT_EQ(arg.parasitic(), fake); arg.setParasitic(nullptr); EXPECT_EQ(arg.parasitic(), nullptr); } // ArcDcalcResult: zero loads TEST_F(ArcDcalcResultTest, ZeroLoads) { ArcDcalcResult result; result.setGateDelay(1e-10f); result.setDrvrSlew(5e-11f); EXPECT_FLOAT_EQ(delayAsFloat(result.gateDelay()), 1e-10f); EXPECT_FLOAT_EQ(delayAsFloat(result.drvrSlew()), 5e-11f); } // ArcDcalcResult: single load TEST_F(ArcDcalcResultTest, SingleLoad2) { ArcDcalcResult result(1); result.setGateDelay(2e-10f); result.setDrvrSlew(1e-10f); result.setWireDelay(0, 5e-12f); result.setLoadSlew(0, 1.5e-10f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(0)), 5e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(0)), 1.5e-10f); } // ArcDcalcResult: setLoadCount from zero TEST_F(ArcDcalcResultTest, SetLoadCountFromZero) { ArcDcalcResult result; result.setLoadCount(3); result.setWireDelay(0, 1e-12f); result.setWireDelay(1, 2e-12f); result.setWireDelay(2, 3e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(2)), 3e-12f); } // Test all calcs: name() returns non-empty string TEST_F(StaDcalcTest, AllCalcsName) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr) << "Failed for: " << name; EXPECT_NE(calc->name(), nullptr) << "Null name for: " << name; EXPECT_GT(strlen(calc->name()), 0u) << "Empty name for: " << name; delete calc; } } // Test all calcs: reduceSupported returns a bool TEST_F(StaDcalcTest, AllCalcsReduceSupported2) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr) << "Failed for: " << name; // Just call it - it returns true or false bool supported = calc->reduceSupported(); (void)supported; delete calc; } } // Test all calcs: copy() produces a valid calc TEST_F(StaDcalcTest, AllCalcsCopy) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr) << "Failed for: " << name; ArcDelayCalc *copy = calc->copy(); ASSERT_NE(copy, nullptr) << "Copy failed for: " << name; EXPECT_STREQ(copy->name(), calc->name()); delete copy; delete calc; } } // FindRoot: quadratic function with exact root TEST_F(FindRootAdditionalTest, QuadraticExact) { FindRootFunc func = [](double x, double &y, double &dy) { y = x * x - 4.0; dy = 2.0 * x; }; bool fail = false; double root = findRoot(func, 1.0, 3.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 2.0, 1e-8); } // FindRoot: 4-arg overload with quadratic TEST_F(FindRootAdditionalTest, QuadraticFourArg) { FindRootFunc func = [](double x, double &y, double &dy) { y = x * x - 9.0; dy = 2.0 * x; }; bool fail = false; // y(2.5) = 6.25-9 = -2.75, y(3.5) = 12.25-9 = 3.25 double root = findRoot(func, 2.5, -2.75, 3.5, 3.25, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 3.0, 1e-8); } //////////////////////////////////////////////////////////////// // R8_ tests for additional dcalc coverage //////////////////////////////////////////////////////////////// // R8_InEdgeNull removed (segfault - dereferences null edge) // R8_DrvrVertexNull removed (segfault - dereferences null pin) // R8_DrvrNetNull removed (segfault - dereferences null pin) // ArcDcalcArg: multiple set/get with edge cases TEST_F(ArcDcalcArgTest, ZeroLoadCap) { ArcDcalcArg arg; arg.setLoadCap(0.0f); EXPECT_FLOAT_EQ(arg.loadCap(), 0.0f); } TEST_F(ArcDcalcArgTest, NegativeInputDelay) { ArcDcalcArg arg; arg.setInputDelay(-1.0e-9f); EXPECT_FLOAT_EQ(arg.inputDelay(), -1.0e-9f); } TEST_F(ArcDcalcArgTest, VeryLargeLoadCap) { ArcDcalcArg arg; arg.setLoadCap(1.0e-3f); EXPECT_FLOAT_EQ(arg.loadCap(), 1.0e-3f); } TEST_F(ArcDcalcArgTest, VerySmallSlew) { ArcDcalcArg arg; arg.setInSlew(1.0e-15f); EXPECT_FLOAT_EQ(arg.inSlewFlt(), 1.0e-15f); } // ArcDcalcResult: edge cases TEST_F(ArcDcalcResultTest, NegativeGateDelay) { ArcDcalcResult result; result.setGateDelay(-1.0e-10f); EXPECT_FLOAT_EQ(delayAsFloat(result.gateDelay()), -1.0e-10f); } TEST_F(ArcDcalcResultTest, VeryLargeWireDelay) { ArcDcalcResult result(1); result.setWireDelay(0, 1.0e-3f); EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(0)), 1.0e-3f); } TEST_F(ArcDcalcResultTest, ZeroDrvrSlew) { ArcDcalcResult result; result.setDrvrSlew(0.0f); EXPECT_FLOAT_EQ(delayAsFloat(result.drvrSlew()), 0.0f); } TEST_F(ArcDcalcResultTest, MultipleLoadSetGet) { ArcDcalcResult result(5); for (size_t i = 0; i < 5; i++) { float delay = static_cast(i + 1) * 1e-12f; float slew = static_cast(i + 1) * 10e-12f; result.setWireDelay(i, delay); result.setLoadSlew(i, slew); } for (size_t i = 0; i < 5; i++) { float delay = static_cast(i + 1) * 1e-12f; float slew = static_cast(i + 1) * 10e-12f; EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(i)), delay); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(i)), slew); } } // NetCaps additional coverage - default constructor doesn't zero-init TEST_F(StaDcalcTest, NetCapsDefaultConstructorExists) { ASSERT_NO_THROW(( [&](){ NetCaps caps; // Default constructor doesn't initialize members, just verify construction SUCCEED(); }() )); } TEST_F(StaDcalcTest, NetCapsParameterizedConstructor) { NetCaps caps(1.0e-12f, 2.0e-12f, 3.0f, true); EXPECT_FLOAT_EQ(caps.pinCap(), 1.0e-12f); EXPECT_FLOAT_EQ(caps.wireCap(), 2.0e-12f); EXPECT_FLOAT_EQ(caps.fanout(), 3.0f); EXPECT_TRUE(caps.hasNetLoad()); } TEST_F(StaDcalcTest, NetCapsInit) { NetCaps caps; caps.init(5.0e-12f, 10.0e-12f, 2.0f, true); EXPECT_FLOAT_EQ(caps.pinCap(), 5.0e-12f); EXPECT_FLOAT_EQ(caps.wireCap(), 10.0e-12f); EXPECT_FLOAT_EQ(caps.fanout(), 2.0f); EXPECT_TRUE(caps.hasNetLoad()); } TEST_F(StaDcalcTest, NetCapsInitZero) { NetCaps caps(1.0f, 2.0f, 3.0f, true); caps.init(0.0f, 0.0f, 0.0f, false); EXPECT_FLOAT_EQ(caps.pinCap(), 0.0f); EXPECT_FLOAT_EQ(caps.wireCap(), 0.0f); EXPECT_FLOAT_EQ(caps.fanout(), 0.0f); EXPECT_FALSE(caps.hasNetLoad()); } TEST_F(StaDcalcTest, NetCapsLargeValues2) { NetCaps caps(100.0e-12f, 200.0e-12f, 50.0f, true); EXPECT_FLOAT_EQ(caps.pinCap(), 100.0e-12f); EXPECT_FLOAT_EQ(caps.wireCap(), 200.0e-12f); EXPECT_FLOAT_EQ(caps.fanout(), 50.0f); } // GraphDelayCalc additional coverage TEST_F(StaDcalcTest, GraphDelayCalcConstruct) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); EXPECT_NE(gdc, nullptr); } TEST_F(StaDcalcTest, GraphDelayCalcClear3) { ASSERT_NO_THROW(( [&](){ GraphDelayCalc *gdc = sta_->graphDelayCalc(); gdc->clear(); SUCCEED(); }() )); } TEST_F(StaDcalcTest, GraphDelayCalcDelaysInvalid3) { ASSERT_NO_THROW(( [&](){ GraphDelayCalc *gdc = sta_->graphDelayCalc(); gdc->delaysInvalid(); SUCCEED(); }() )); } TEST_F(StaDcalcTest, GraphDelayCalcSetObserver) { ASSERT_NO_THROW(( [&](){ GraphDelayCalc *gdc = sta_->graphDelayCalc(); gdc->setObserver(nullptr); SUCCEED(); }() )); } TEST_F(StaDcalcTest, GraphDelayCalcLevelsChanged) { ASSERT_NO_THROW(( [&](){ GraphDelayCalc *gdc = sta_->graphDelayCalc(); gdc->levelsChangedBefore(); SUCCEED(); }() )); } TEST_F(StaDcalcTest, GraphDelayCalcCopyState3) { ASSERT_NO_THROW(( [&](){ GraphDelayCalc *gdc = sta_->graphDelayCalc(); gdc->copyState(sta_); SUCCEED(); }() )); } TEST_F(StaDcalcTest, GraphDelayCalcIncrTolerance) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); float tol = gdc->incrementalDelayTolerance(); EXPECT_GE(tol, 0.0f); gdc->setIncrementalDelayTolerance(0.05f); EXPECT_FLOAT_EQ(gdc->incrementalDelayTolerance(), 0.05f); gdc->setIncrementalDelayTolerance(tol); } // R8_AllCalcsFindParasitic removed (segfault - some calcs dereference null DcalcAnalysisPt) // R8_AllCalcsReduceParasiticNull removed (segfault) // R8_AllCalcsCheckDelay removed (segfault - some calcs dereference null arc) // R8_AllCalcsGateDelayNull removed (segfault - some calcs dereference null arc) // R8_AllCalcsReportGateDelay removed (segfault) // R8_AllCalcsReportCheckDelay removed (segfault) // FindRoot: additional edge cases TEST_F(FindRootAdditionalTest, LinearFunction2) { FindRootFunc func = [](double x, double &y, double &dy) { y = 2.0 * x - 10.0; dy = 2.0; }; bool fail = false; double root = findRoot(func, 0.0, 10.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 5.0, 1e-8); } TEST_F(FindRootAdditionalTest, FourArgLinear) { FindRootFunc func = [](double x, double &y, double &dy) { y = 3.0 * x - 6.0; dy = 3.0; }; bool fail = false; // y(1.0) = -3, y(3.0) = 3 double root = findRoot(func, 1.0, -3.0, 3.0, 3.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 2.0, 1e-8); } TEST_F(FindRootAdditionalTest, HighOrderPoly) { FindRootFunc func = [](double x, double &y, double &dy) { y = x * x * x * x - 16.0; dy = 4.0 * x * x * x; }; bool fail = false; double root = findRoot(func, 1.0, 3.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 2.0, 1e-6); } TEST_F(FindRootAdditionalTest, NegativeRoot) { FindRootFunc func = [](double x, double &y, double &dy) { y = x + 3.0; dy = 1.0; }; bool fail = false; double root = findRoot(func, -5.0, -1.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, -3.0, 1e-8); } TEST_F(FindRootAdditionalTest, TrigFunction) { FindRootFunc func = [](double x, double &y, double &dy) { y = std::cos(x); dy = -std::sin(x); }; bool fail = false; // Root at pi/2 double root = findRoot(func, 1.0, 2.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, M_PI / 2.0, 1e-8); } TEST_F(FindRootAdditionalTest, VeryTightBounds) { FindRootFunc func = [](double x, double &y, double &dy) { y = x - 5.0; dy = 1.0; }; bool fail = false; double root = findRoot(func, 4.999, 5.001, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 5.0, 1e-8); } TEST_F(FindRootAdditionalTest, ExpFunction) { FindRootFunc func = [](double x, double &y, double &dy) { y = std::exp(x) - 10.0; dy = std::exp(x); }; bool fail = false; double root = findRoot(func, 1.0, 3.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, std::log(10.0), 1e-8); } TEST_F(FindRootAdditionalTest, FourArgSwap) { FindRootFunc func = [](double x, double &y, double &dy) { y = x - 7.0; dy = 1.0; }; bool fail = false; // y1 = 3.0 > 0, y2 = -7.0 < 0 => internal swap double root = findRoot(func, 10.0, 3.0, 0.0, -7.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 7.0, 1e-8); } // DesignDcalcTest: additional delay calculator exercises on real design TEST_F(DesignDcalcTest, TimingLumpedCap2) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("lumped_cap"); sta_->updateTiming(true); SUCCEED(); } TEST_F(DesignDcalcTest, TimingUnit2) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("unit"); sta_->updateTiming(true); SUCCEED(); } TEST_F(DesignDcalcTest, TimingArnoldi2) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("arnoldi"); sta_->updateTiming(true); SUCCEED(); } TEST_F(DesignDcalcTest, FindDelaysDmpElmore) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); // Verify we can get a delay value GraphDelayCalc *gdc = sta_->graphDelayCalc(); EXPECT_NE(gdc, nullptr); SUCCEED(); } TEST_F(DesignDcalcTest, FindDelaysDmpTwoPole2) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_two_pole"); sta_->updateTiming(true); SUCCEED(); } TEST_F(DesignDcalcTest, FindDelaysCcsCeff2) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("ccs_ceff"); sta_->updateTiming(true); SUCCEED(); } TEST_F(DesignDcalcTest, FindDelaysPrima2) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("prima"); sta_->updateTiming(true); SUCCEED(); } // R8_LumpedCapFindParasitic removed (segfault - needs DcalcAnalysisPt) // R8_LumpedCapReduceParasitic removed (segfault) // R8_LumpedCapCheckDelay removed (segfault - dereferences null arc) // R8_LumpedCapGateDelay removed (segfault - dereferences null arc) // R8_LumpedCapReportGateDelay removed (segfault) // LumpedCap: safe exercises that don't need real timing arcs TEST_F(StaDcalcTest, LumpedCapFinishDrvrPin2) { ArcDelayCalc *calc = makeDelayCalc("lumped_cap", sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); delete calc; } TEST_F(StaDcalcTest, LumpedCapCopyState2) { ArcDelayCalc *calc = makeDelayCalc("lumped_cap", sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); EXPECT_STREQ(calc->name(), "lumped_cap"); delete calc; } // R8_DmpCeffElmoreFindParasitic removed (segfault) // R8_DmpCeffElmoreInputPortDelay removed (segfault) TEST_F(StaDcalcTest, DmpCeffElmoreFinishDrvrPin2) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_elmore", sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); delete calc; } TEST_F(StaDcalcTest, DmpCeffElmoreCopyState) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_elmore", sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); EXPECT_STREQ(calc->name(), "dmp_ceff_elmore"); delete calc; } // R8_DmpCeffTwoPoleFindParasitic removed (segfault) // R8_DmpCeffTwoPoleInputPortDelay removed (segfault) TEST_F(StaDcalcTest, DmpCeffTwoPoleFinishDrvrPin2) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_two_pole", sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); delete calc; } TEST_F(StaDcalcTest, DmpCeffTwoPoleCopyState) { ArcDelayCalc *calc = makeDelayCalc("dmp_ceff_two_pole", sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); EXPECT_STREQ(calc->name(), "dmp_ceff_two_pole"); delete calc; } // R8_CcsCeffFindParasitic removed (segfault) // R8_CcsCeffInputPortDelay removed (segfault) TEST_F(StaDcalcTest, CcsCeffFinishDrvrPin2) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); delete calc; } TEST_F(StaDcalcTest, CcsCeffCopyState2) { ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); EXPECT_STREQ(calc->name(), "ccs_ceff"); delete calc; } // R8_PrimaFindParasitic removed (segfault) // R8_PrimaInputPortDelay removed (segfault) TEST_F(StaDcalcTest, PrimaCopyState2) { ArcDelayCalc *calc = makeDelayCalc("prima", sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); EXPECT_STREQ(calc->name(), "prima"); delete calc; } // ArcDcalcArg: FullConstructor variants TEST_F(ArcDcalcArgTest, FullConstructorAllZeros) { ArcDcalcArg arg(nullptr, nullptr, nullptr, nullptr, 0.0f, 0.0f, nullptr); EXPECT_FLOAT_EQ(arg.inSlewFlt(), 0.0f); EXPECT_FLOAT_EQ(arg.loadCap(), 0.0f); EXPECT_FLOAT_EQ(arg.inputDelay(), 0.0f); } TEST_F(ArcDcalcArgTest, InputDelayConstructorZero) { ArcDcalcArg arg(nullptr, nullptr, nullptr, nullptr, 0.0f); EXPECT_FLOAT_EQ(arg.inputDelay(), 0.0f); } TEST_F(ArcDcalcArgTest, CopyAssignment) { ArcDcalcArg arg; arg.setLoadCap(3.0e-12f); arg.setInputDelay(2.0e-9f); arg.setInSlew(75e-12f); ArcDcalcArg copy; copy = arg; EXPECT_FLOAT_EQ(copy.loadCap(), 3.0e-12f); EXPECT_FLOAT_EQ(copy.inputDelay(), 2.0e-9f); EXPECT_FLOAT_EQ(copy.inSlewFlt(), 75e-12f); } // ArcDcalcResult: copy construction TEST_F(ArcDcalcResultTest, CopyConstruction) { ArcDcalcResult result(3); result.setGateDelay(1e-10f); result.setDrvrSlew(2e-10f); result.setWireDelay(0, 1e-12f); result.setWireDelay(1, 2e-12f); result.setWireDelay(2, 3e-12f); result.setLoadSlew(0, 10e-12f); result.setLoadSlew(1, 20e-12f); result.setLoadSlew(2, 30e-12f); ArcDcalcResult copy(result); EXPECT_FLOAT_EQ(delayAsFloat(copy.gateDelay()), 1e-10f); EXPECT_FLOAT_EQ(delayAsFloat(copy.drvrSlew()), 2e-10f); EXPECT_FLOAT_EQ(delayAsFloat(copy.wireDelay(0)), 1e-12f); EXPECT_FLOAT_EQ(delayAsFloat(copy.wireDelay(2)), 3e-12f); EXPECT_FLOAT_EQ(delayAsFloat(copy.loadSlew(1)), 20e-12f); } // ArcDcalcArgSeq operations TEST_F(ArcDcalcArgTest, ArgSeqOperations) { ArcDcalcArgSeq args; for (int i = 0; i < 5; i++) { ArcDcalcArg arg; arg.setLoadCap(static_cast(i) * 1e-12f); args.push_back(arg); } EXPECT_EQ(args.size(), 5u); for (int i = 0; i < 5; i++) { EXPECT_FLOAT_EQ(args[i].loadCap(), static_cast(i) * 1e-12f); } } // R8_AllCalcsGateDelaysEmpty removed (segfault - some calcs deref DcalcAnalysisPt) // R8_AllCalcsReduceParasiticNet removed (segfault) // All delay calcs: setDcalcArgParasiticSlew (single and seq) TEST_F(StaDcalcTest, AllCalcsSetDcalcArgParasitic) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr) << "Failed for: " << name; ArcDcalcArg arg; calc->setDcalcArgParasiticSlew(arg, nullptr); ArcDcalcArgSeq args; args.push_back(ArcDcalcArg()); calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } } //////////////////////////////////////////////////////////////// // R9_ tests for dcalc coverage improvement //////////////////////////////////////////////////////////////// // R9_ Test reportDelayCalc with dmp_ceff_elmore (exercises gateDelaySlew) TEST_F(DesignDcalcTest, ReportDelayCalcDmpElmore) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); bool found = false; VertexIterator viter(graph); while (viter.hasNext()) { Vertex *v = viter.next(); VertexInEdgeIterator eiter(v, graph); while (eiter.hasNext()) { Edge *edge = eiter.next(); for (auto arc : edge->timingArcSet()->arcs()) { Corner *corner = sta_->cmdCorner(); std::string report = sta_->reportDelayCalc(edge, arc, corner, MinMax::max(), 4); EXPECT_FALSE(report.empty()); found = true; break; } if (found) break; } if (found) break; } EXPECT_TRUE(found); } // R9_ Test reportDelayCalc with dmp_ceff_two_pole TEST_F(DesignDcalcTest, ReportDelayCalcDmpTwoPole) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_two_pole"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); bool found = false; VertexIterator viter(graph); while (viter.hasNext()) { Vertex *v = viter.next(); VertexInEdgeIterator eiter(v, graph); while (eiter.hasNext()) { Edge *edge = eiter.next(); for (auto arc : edge->timingArcSet()->arcs()) { Corner *corner = sta_->cmdCorner(); std::string report = sta_->reportDelayCalc(edge, arc, corner, MinMax::max(), 4); EXPECT_FALSE(report.empty()); found = true; break; } if (found) break; } if (found) break; } EXPECT_TRUE(found); } // R9_ Test reportDelayCalc with ccs_ceff TEST_F(DesignDcalcTest, ReportDelayCalcCcsCeff) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("ccs_ceff"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); bool found = false; VertexIterator viter(graph); while (viter.hasNext()) { Vertex *v = viter.next(); VertexInEdgeIterator eiter(v, graph); while (eiter.hasNext()) { Edge *edge = eiter.next(); for (auto arc : edge->timingArcSet()->arcs()) { Corner *corner = sta_->cmdCorner(); std::string report = sta_->reportDelayCalc(edge, arc, corner, MinMax::max(), 4); EXPECT_FALSE(report.empty()); found = true; break; } if (found) break; } if (found) break; } EXPECT_TRUE(found); } // R9_ Test reportDelayCalc with lumped_cap TEST_F(DesignDcalcTest, ReportDelayCalcLumpedCap) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("lumped_cap"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); bool found = false; VertexIterator viter(graph); while (viter.hasNext()) { Vertex *v = viter.next(); VertexInEdgeIterator eiter(v, graph); while (eiter.hasNext()) { Edge *edge = eiter.next(); for (auto arc : edge->timingArcSet()->arcs()) { Corner *corner = sta_->cmdCorner(); std::string report = sta_->reportDelayCalc(edge, arc, corner, MinMax::max(), 4); EXPECT_FALSE(report.empty()); found = true; break; } if (found) break; } if (found) break; } EXPECT_TRUE(found); } // R9_ Test reportDelayCalc with arnoldi TEST_F(DesignDcalcTest, ReportDelayCalcArnoldi) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("arnoldi"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); bool found = false; VertexIterator viter(graph); while (viter.hasNext()) { Vertex *v = viter.next(); VertexInEdgeIterator eiter(v, graph); while (eiter.hasNext()) { Edge *edge = eiter.next(); for (auto arc : edge->timingArcSet()->arcs()) { Corner *corner = sta_->cmdCorner(); std::string report = sta_->reportDelayCalc(edge, arc, corner, MinMax::max(), 4); EXPECT_FALSE(report.empty()); found = true; break; } if (found) break; } if (found) break; } EXPECT_TRUE(found); } // R9_ Test reportDelayCalc with prima TEST_F(DesignDcalcTest, ReportDelayCalcPrima) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("prima"); Corner *corner = sta_->cmdCorner(); Instance *top = sta_->network()->topInstance(); sta_->readSpef("test/reg1_asap7.spef", top, corner, MinMaxAll::all(), false, false, 1.0f, false); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); bool found = false; VertexIterator viter(graph); while (viter.hasNext()) { Vertex *v = viter.next(); VertexInEdgeIterator eiter(v, graph); while (eiter.hasNext()) { Edge *edge = eiter.next(); for (auto arc : edge->timingArcSet()->arcs()) { std::string report = sta_->reportDelayCalc(edge, arc, corner, MinMax::max(), 4); EXPECT_FALSE(report.empty()); found = true; break; } if (found) break; } if (found) break; } EXPECT_TRUE(found); } // R9_ Test incremental timing with different calculators TEST_F(DesignDcalcTest, IncrementalDmpTwoPole) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_two_pole"); sta_->updateTiming(true); sta_->updateTiming(false); SUCCEED(); } TEST_F(DesignDcalcTest, IncrementalCcsCeff) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("ccs_ceff"); sta_->updateTiming(true); sta_->updateTiming(false); SUCCEED(); } TEST_F(DesignDcalcTest, IncrementalLumpedCap) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("lumped_cap"); sta_->updateTiming(true); sta_->updateTiming(false); SUCCEED(); } TEST_F(DesignDcalcTest, IncrementalArnoldi) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("arnoldi"); sta_->updateTiming(true); sta_->updateTiming(false); SUCCEED(); } TEST_F(DesignDcalcTest, IncrementalPrima) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("prima"); sta_->updateTiming(true); sta_->updateTiming(false); SUCCEED(); } // R9_ Cycle through all calculators TEST_F(DesignDcalcTest, CycleAllCalcs) { ASSERT_TRUE(design_loaded_); const char *calcs[] = {"unit", "lumped_cap", "dmp_ceff_elmore", "dmp_ceff_two_pole", "arnoldi", "ccs_ceff", "prima"}; for (const char *name : calcs) { sta_->setArcDelayCalc(name); sta_->updateTiming(true); } SUCCEED(); } // R9_ReportMultipleEdges removed (segfault) // R9_ Test findDelays then verify graph vertices have edge delays TEST_F(DesignDcalcTest, VerifyEdgeDelays) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); int edges_with_delays = 0; VertexIterator viter(graph); while (viter.hasNext() && edges_with_delays < 5) { Vertex *v = viter.next(); VertexInEdgeIterator eiter(v, graph); while (eiter.hasNext()) { Edge *edge = eiter.next(); (void)edge; edges_with_delays++; break; } } EXPECT_GT(edges_with_delays, 0); } // R9_ Test min analysis report TEST_F(DesignDcalcTest, MinAnalysisReport) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); bool found = false; VertexIterator viter(graph); while (viter.hasNext()) { Vertex *v = viter.next(); VertexInEdgeIterator eiter(v, graph); while (eiter.hasNext()) { Edge *edge = eiter.next(); for (auto arc : edge->timingArcSet()->arcs()) { Corner *corner = sta_->cmdCorner(); std::string report = sta_->reportDelayCalc(edge, arc, corner, MinMax::min(), 4); if (!report.empty()) found = true; break; } if (found) break; } if (found) break; } EXPECT_TRUE(found); } // R9_ Test arnoldi reduce on design TEST_F(DesignDcalcTest, ArnoldiReduceDesign) { ASSERT_TRUE(design_loaded_); Corner *corner = sta_->cmdCorner(); Instance *top = sta_->network()->topInstance(); sta_->readSpef("test/reg1_asap7.spef", top, corner, MinMaxAll::all(), false, false, 1.0f, false); ArcDelayCalc *calc = makeDelayCalc("arnoldi", sta_); ASSERT_NE(calc, nullptr); Network *network = sta_->network(); Parasitics *parasitics = sta_->parasitics(); const MinMax *mm = MinMax::max(); DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm); InstanceChildIterator *child_iter = network->childIterator(top); int reduced_count = 0; while (child_iter->hasNext() && reduced_count < 3) { Instance *child = child_iter->next(); InstancePinIterator *pin_iter = network->pinIterator(child); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); if (network->isDriver(pin)) { const Net *net = network->net(pin); if (net) { Parasitic *pnet = parasitics->findParasiticNetwork(net, ap); if (pnet) { for (auto rf : RiseFall::range()) { Parasitic *reduced = calc->reduceParasitic(pnet, pin, rf, dcalc_ap); (void)reduced; } reduced_count++; } } } } delete pin_iter; } delete child_iter; delete calc; SUCCEED(); } // R9_ CcsCeff watchPin with design pin TEST_F(DesignDcalcTest, CcsCeffWatchPinDesign) { ASSERT_TRUE(design_loaded_); ArcDelayCalc *calc = makeDelayCalc("ccs_ceff", sta_); CcsCeffDelayCalc *ccs = dynamic_cast(calc); ASSERT_NE(ccs, nullptr); Network *network = sta_->network(); Instance *top = network->topInstance(); Pin *out = network->findPin(top, "out"); if (out) { ccs->watchPin(out); EXPECT_EQ(ccs->watchPins().size(), 1u); ccs->clearWatchPins(); EXPECT_TRUE(ccs->watchPins().empty()); } delete calc; } // R9_ PrimaDelayCalc watchPin with design pin TEST_F(DesignDcalcTest, PrimaWatchPinDesign) { ASSERT_TRUE(design_loaded_); ArcDelayCalc *calc = makeDelayCalc("prima", sta_); PrimaDelayCalc *prima = dynamic_cast(calc); ASSERT_NE(prima, nullptr); Network *network = sta_->network(); Instance *top = network->topInstance(); Pin *out = network->findPin(top, "out"); if (out) { prima->watchPin(out); EXPECT_EQ(prima->watchPins().size(), 1u); prima->clearWatchPins(); EXPECT_TRUE(prima->watchPins().empty()); } delete calc; } // R9_ Test setIncrementalDelayTolerance + retiming TEST_F(DesignDcalcTest, IncrTolRetiming) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->setIncrementalDelayTolerance(0.01f); sta_->updateTiming(true); sta_->setIncrementalDelayTolerance(0.0f); sta_->updateTiming(true); SUCCEED(); } // R9_ Test findDelays with graph verification TEST_F(DesignDcalcTest, FindDelaysVerifyGraph) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->findDelays(); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 10); } // R9_ NetCaps very small values TEST_F(StaDcalcTest, NetCapsVerySmall) { NetCaps caps; caps.init(1e-18f, 2e-18f, 0.001f, true); EXPECT_FLOAT_EQ(caps.pinCap(), 1e-18f); EXPECT_FLOAT_EQ(caps.wireCap(), 2e-18f); EXPECT_TRUE(caps.hasNetLoad()); } // R9_ NetCaps negative values TEST_F(StaDcalcTest, NetCapsNegative) { NetCaps caps; caps.init(-1e-12f, -2e-12f, -1.0f, false); EXPECT_FLOAT_EQ(caps.pinCap(), -1e-12f); EXPECT_FALSE(caps.hasNetLoad()); } // R9_ ArcDcalcArg full constructor with all non-null TEST_F(ArcDcalcArgTest, FullConstructorNonNull) { int d1=1,d2=2,d3=3,d4=4,d5=5; ArcDcalcArg arg(reinterpret_cast(&d1), reinterpret_cast(&d2), reinterpret_cast(&d3), reinterpret_cast(&d4), 100e-12f, 5e-12f, reinterpret_cast(&d5)); EXPECT_NE(arg.inPin(), nullptr); EXPECT_NE(arg.drvrPin(), nullptr); EXPECT_NE(arg.edge(), nullptr); EXPECT_NE(arg.arc(), nullptr); EXPECT_NE(arg.parasitic(), nullptr); arg.setLoadCap(10e-12f); arg.setInSlew(200e-12f); arg.setInputDelay(5e-9f); arg.setParasitic(nullptr); EXPECT_FLOAT_EQ(arg.loadCap(), 10e-12f); EXPECT_EQ(arg.parasitic(), nullptr); } // R9_ ArcDcalcResult large load count ops TEST_F(ArcDcalcResultTest, LargeLoadCountOps) { ArcDcalcResult result(50); result.setGateDelay(1e-9f); result.setDrvrSlew(5e-10f); for (size_t i = 0; i < 50; i++) { result.setWireDelay(i, static_cast(i) * 0.1e-12f); result.setLoadSlew(i, static_cast(i) * 1e-12f); } EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(49)), 4.9e-12f); EXPECT_FLOAT_EQ(delayAsFloat(result.loadSlew(49)), 49e-12f); } // R9_ ArcDcalcResult: resize multiple times TEST_F(ArcDcalcResultTest, ResizeMultiple) { ArcDcalcResult result; for (int s = 1; s <= 10; s++) { result.setLoadCount(s); result.setWireDelay(s-1, static_cast(s) * 1e-12f); result.setLoadSlew(s-1, static_cast(s) * 10e-12f); } EXPECT_FLOAT_EQ(delayAsFloat(result.wireDelay(9)), 10e-12f); } // R9_ ArcDcalcResultSeq operations TEST_F(ArcDcalcResultTest, ResultSeqOps) { ArcDcalcResultSeq results; for (int i = 0; i < 10; i++) { ArcDcalcResult r(3); r.setGateDelay(static_cast(i) * 1e-10f); results.push_back(r); } EXPECT_EQ(results.size(), 10u); EXPECT_FLOAT_EQ(delayAsFloat(results[5].gateDelay()), 5e-10f); } // R9_ FindRoot: steep derivative TEST_F(FindRootAdditionalTest, SteepDerivative) { FindRootFunc func = [](double x, double &y, double &dy) { y = 1000.0 * x - 500.0; dy = 1000.0; }; bool fail = false; double root = findRoot(func, 0.0, 1.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 0.5, 1e-8); } // R9_ FindRoot: quartic function TEST_F(FindRootAdditionalTest, QuarticRoot) { FindRootFunc func = [](double x, double &y, double &dy) { y = x*x*x*x - 81.0; dy = 4.0*x*x*x; }; bool fail = false; double root = findRoot(func, 2.0, 4.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, 3.0, 1e-6); } // R9_ FindRoot: 4-arg negative bracket TEST_F(FindRootAdditionalTest, FourArgNegBracket) { FindRootFunc func = [](double x, double &y, double &dy) { y = x + 5.0; dy = 1.0; }; bool fail = false; double root = findRoot(func, -8.0, -3.0, -2.0, 3.0, 1e-10, 100, fail); EXPECT_FALSE(fail); EXPECT_NEAR(root, -5.0, 1e-8); } // R9_ MultiDrvrNet set and reset TEST_F(StaDcalcTest, MultiDrvrNetSetReset) { MultiDrvrNet mdn; int d1=1,d2=2; mdn.setDcalcDrvr(reinterpret_cast(&d1)); EXPECT_EQ(mdn.dcalcDrvr(), reinterpret_cast(&d1)); mdn.setDcalcDrvr(reinterpret_cast(&d2)); EXPECT_EQ(mdn.dcalcDrvr(), reinterpret_cast(&d2)); mdn.setDcalcDrvr(nullptr); EXPECT_EQ(mdn.dcalcDrvr(), nullptr); } // R9_ All calcs copyState twice TEST_F(StaDcalcTest, AllCalcsCopyStateTwice) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); calc->copyState(sta_); delete calc; } } // R9_ GraphDelayCalc levels then clear TEST_F(StaDcalcTest, GraphDelayCalcLevelsClear) { GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); gdc->levelsChangedBefore(); gdc->clear(); SUCCEED(); } // R9_ All calcs inputPortDelay with non-zero slew TEST_F(StaDcalcTest, AllCalcsInputPortDelaySlew) { StringSeq names = delayCalcNames(); for (const char *name : names) { ArcDelayCalc *calc = makeDelayCalc(name, sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->inputPortDelay(nullptr, 100e-12, nullptr, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } } //////////////////////////////////////////////////////////////// // R10_ tests for additional dcalc coverage //////////////////////////////////////////////////////////////// // R10_ DmpCeffElmore: explicit make/delete exercises constructor/destructor // Covers: DmpCeffElmoreDelayCalc::DmpCeffElmoreDelayCalc, DmpCeffDelayCalc::~DmpCeffDelayCalc TEST_F(StaDcalcTest, DmpCeffElmoreMakeDelete) { ArcDelayCalc *calc = makeDmpCeffElmoreDelayCalc(sta_); ASSERT_NE(calc, nullptr); EXPECT_STREQ(calc->name(), "dmp_ceff_elmore"); EXPECT_TRUE(calc->reduceSupported()); delete calc; } // R10_ DmpCeffTwoPole: explicit make/delete exercises constructor/destructor // Covers: DmpCeffTwoPoleDelayCalc::DmpCeffTwoPoleDelayCalc, DmpCeffDelayCalc::~DmpCeffDelayCalc TEST_F(StaDcalcTest, DmpCeffTwoPoleMakeDelete) { ArcDelayCalc *calc = makeDmpCeffTwoPoleDelayCalc(sta_); ASSERT_NE(calc, nullptr); EXPECT_STREQ(calc->name(), "dmp_ceff_two_pole"); EXPECT_TRUE(calc->reduceSupported()); delete calc; } // R10_ DmpCeffElmore: copy exercises copy constructor chain // Covers: DmpCeffElmoreDelayCalc::copy -> DmpCeffElmoreDelayCalc(StaState*) TEST_F(StaDcalcTest, DmpCeffElmoreCopy2) { ArcDelayCalc *calc = makeDmpCeffElmoreDelayCalc(sta_); ASSERT_NE(calc, nullptr); ArcDelayCalc *copy = calc->copy(); ASSERT_NE(copy, nullptr); EXPECT_STREQ(copy->name(), "dmp_ceff_elmore"); delete copy; delete calc; } // R10_ DmpCeffTwoPole: copy exercises copy constructor chain // Covers: DmpCeffTwoPoleDelayCalc::copy TEST_F(StaDcalcTest, DmpCeffTwoPoleCopy2) { ArcDelayCalc *calc = makeDmpCeffTwoPoleDelayCalc(sta_); ASSERT_NE(calc, nullptr); ArcDelayCalc *copy = calc->copy(); ASSERT_NE(copy, nullptr); EXPECT_STREQ(copy->name(), "dmp_ceff_two_pole"); delete copy; delete calc; } // R10_ DmpCeffElmore: copyState exercises DmpCeffDelayCalc::copyState TEST_F(StaDcalcTest, DmpCeffElmoreCopyState2) { ArcDelayCalc *calc = makeDmpCeffElmoreDelayCalc(sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); EXPECT_STREQ(calc->name(), "dmp_ceff_elmore"); delete calc; } // R10_ DmpCeffTwoPole: copyState exercises DmpCeffDelayCalc::copyState TEST_F(StaDcalcTest, DmpCeffTwoPoleCopyState2) { ArcDelayCalc *calc = makeDmpCeffTwoPoleDelayCalc(sta_); ASSERT_NE(calc, nullptr); calc->copyState(sta_); EXPECT_STREQ(calc->name(), "dmp_ceff_two_pole"); delete calc; } // R10_ DmpCeffElmore inputPortDelay with null args // Covers: DmpCeffElmoreDelayCalc::inputPortDelay TEST_F(StaDcalcTest, DmpCeffElmoreInputPortDelay2) { ArcDelayCalc *calc = makeDmpCeffElmoreDelayCalc(sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->inputPortDelay(nullptr, 50e-12, nullptr, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } // R10_ DmpCeffTwoPole inputPortDelay with null args // Covers: DmpCeffTwoPoleDelayCalc::inputPortDelay TEST_F(StaDcalcTest, DmpCeffTwoPoleInputPortDelay2) { ArcDelayCalc *calc = makeDmpCeffTwoPoleDelayCalc(sta_); ASSERT_NE(calc, nullptr); LoadPinIndexMap load_pin_index_map(sta_->network()); ArcDcalcResult result = calc->inputPortDelay(nullptr, 50e-12, nullptr, nullptr, load_pin_index_map, nullptr); EXPECT_GE(delayAsFloat(result.gateDelay()), 0.0f); delete calc; } // R10_ DmpCeffElmore: setDcalcArgParasiticSlew with empty args TEST_F(StaDcalcTest, DmpCeffElmoreSetDcalcArgEmpty) { ArcDelayCalc *calc = makeDmpCeffElmoreDelayCalc(sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } // R10_ DmpCeffTwoPole: setDcalcArgParasiticSlew with empty args TEST_F(StaDcalcTest, DmpCeffTwoPoleSetDcalcArgEmpty) { ArcDelayCalc *calc = makeDmpCeffTwoPoleDelayCalc(sta_); ASSERT_NE(calc, nullptr); ArcDcalcArgSeq args; calc->setDcalcArgParasiticSlew(args, nullptr); delete calc; } // R10_ DmpCeffElmore: finishDrvrPin doesn't crash TEST_F(StaDcalcTest, DmpCeffElmoreFinishDrvrPin3) { ArcDelayCalc *calc = makeDmpCeffElmoreDelayCalc(sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); delete calc; } // R10_ DmpCeffTwoPole: finishDrvrPin doesn't crash TEST_F(StaDcalcTest, DmpCeffTwoPoleFinishDrvrPin3) { ArcDelayCalc *calc = makeDmpCeffTwoPoleDelayCalc(sta_); ASSERT_NE(calc, nullptr); calc->finishDrvrPin(); delete calc; } // R10_ DesignDcalcTest: Full timing with dmp_ceff_elmore then query delays on specific vertex // Covers: GraphDelayCalc::findDelays(Vertex*), initRootSlews, findDriverArcDelays, // zeroSlewAndWireDelays, FindVertexDelays ctor/dtor/copy, // DmpCeffDelayCalc::gateDelaySlew, DmpAlg internal methods TEST_F(DesignDcalcTest, DmpCeffElmoreVertexDelays) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); Instance *u1 = network->findChild(top, "u1"); if (u1) { Pin *y_pin = network->findPin(u1, "Y"); if (y_pin) { Vertex *drv = graph->pinDrvrVertex(y_pin); if (drv) { gdc->findDelays(drv); SUCCEED(); } } } } // R10_ DesignDcalcTest: Full timing with dmp_ceff_two_pole with detailed parasitics // Covers: DmpCeffTwoPoleDelayCalc::loadDelay, gateDelay TEST_F(DesignDcalcTest, DmpCeffTwoPoleWithParasitics) { ASSERT_TRUE(design_loaded_); Corner *corner = sta_->cmdCorner(); Instance *top = sta_->network()->topInstance(); sta_->readSpef("test/reg1_asap7.spef", top, corner, MinMaxAll::all(), false, false, 1.0f, false); sta_->setArcDelayCalc("dmp_ceff_two_pole"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); } // R10_ DesignDcalcTest: reportDelayCalc exercises report path // Covers: GraphDelayCalc::reportDelayCalc TEST_F(DesignDcalcTest, ReportDelayCalcDmpElmore2) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Graph *graph = sta_->graph(); GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); Instance *u2 = network->findChild(top, "u2"); if (u2) { Pin *y_pin = network->findPin(u2, "Y"); if (y_pin) { Vertex *drv = graph->pinDrvrVertex(y_pin); if (drv) { VertexInEdgeIterator edge_iter(drv, graph); if (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); TimingArcSet *arc_set = edge->timingArcSet(); if (arc_set) { for (TimingArc *arc : arc_set->arcs()) { Corner *corner = sta_->cmdCorner(); std::string report = gdc->reportDelayCalc(edge, arc, corner, MinMax::max(), 4); (void)report; break; } } } } } } } // R10_ DesignDcalcTest: loadCap query after timing // Covers: GraphDelayCalc::loadCap variants TEST_F(DesignDcalcTest, LoadCapQuery) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Corner *corner = sta_->cmdCorner(); DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); GraphDelayCalc *gdc = sta_->graphDelayCalc(); Instance *u1 = network->findChild(top, "u1"); if (u1) { Pin *y_pin = network->findPin(u1, "Y"); if (y_pin) { float cap = gdc->loadCap(y_pin, dcalc_ap); EXPECT_GE(cap, 0.0f); float cap_rise = gdc->loadCap(y_pin, RiseFall::rise(), dcalc_ap); EXPECT_GE(cap_rise, 0.0f); float pin_cap, wire_cap; gdc->loadCap(y_pin, RiseFall::rise(), dcalc_ap, pin_cap, wire_cap); EXPECT_GE(pin_cap, 0.0f); EXPECT_GE(wire_cap, 0.0f); } } } // R10_ DesignDcalcTest: netCaps query after timing // Covers: GraphDelayCalc::netCaps TEST_F(DesignDcalcTest, NetCapsQuery) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Corner *corner = sta_->cmdCorner(); DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); GraphDelayCalc *gdc = sta_->graphDelayCalc(); Instance *u1 = network->findChild(top, "u1"); if (u1) { Pin *y_pin = network->findPin(u1, "Y"); if (y_pin) { float pin_cap, wire_cap, fanout; bool has_set_load; gdc->netCaps(y_pin, RiseFall::rise(), dcalc_ap, pin_cap, wire_cap, fanout, has_set_load); EXPECT_GE(pin_cap, 0.0f); EXPECT_GE(wire_cap, 0.0f); EXPECT_GE(fanout, 0.0f); } } } // R10_ DesignDcalcTest: makeLoadPinIndexMap exercises vertex pin mapping // Covers: GraphDelayCalc::makeLoadPinIndexMap TEST_F(DesignDcalcTest, MakeLoadPinIndexMap) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Graph *graph = sta_->graph(); GraphDelayCalc *gdc = sta_->graphDelayCalc(); Instance *u1 = network->findChild(top, "u1"); if (u1) { Pin *y_pin = network->findPin(u1, "Y"); if (y_pin) { Vertex *drv = graph->pinDrvrVertex(y_pin); if (drv) { LoadPinIndexMap map = gdc->makeLoadPinIndexMap(drv); EXPECT_GE(map.size(), 0u); } } } } // R10_ DesignDcalcTest: findDriverArcDelays exercises the public 5-arg overload // Covers: GraphDelayCalc::findDriverArcDelays TEST_F(DesignDcalcTest, FindDriverArcDelays) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Graph *graph = sta_->graph(); GraphDelayCalc *gdc = sta_->graphDelayCalc(); Corner *corner = sta_->cmdCorner(); DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); Instance *u2 = network->findChild(top, "u2"); if (u2) { Pin *y_pin = network->findPin(u2, "Y"); if (y_pin) { Vertex *drv = graph->pinDrvrVertex(y_pin); if (drv) { VertexInEdgeIterator edge_iter(drv, graph); if (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); TimingArcSet *arc_set = edge->timingArcSet(); if (arc_set) { for (TimingArc *arc : arc_set->arcs()) { ArcDelayCalc *calc = makeDmpCeffElmoreDelayCalc(sta_); gdc->findDriverArcDelays(drv, edge, arc, dcalc_ap, calc); delete calc; break; } } } } } } } // R10_ DesignDcalcTest: edgeFromSlew exercises slew lookup (TimingRole overload) // Covers: GraphDelayCalc::edgeFromSlew TEST_F(DesignDcalcTest, EdgeFromSlew) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Graph *graph = sta_->graph(); GraphDelayCalc *gdc = sta_->graphDelayCalc(); Corner *corner = sta_->cmdCorner(); DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); Instance *u2 = network->findChild(top, "u2"); if (u2) { Pin *a_pin = network->findPin(u2, "A"); if (a_pin) { Vertex *v = graph->pinLoadVertex(a_pin); if (v) { // Use the TimingRole* overload const TimingRole *role = TimingRole::combinational(); Slew slew = gdc->edgeFromSlew(v, RiseFall::rise(), role, dcalc_ap); (void)slew; } } } } // R10_ DesignDcalcTest: incremental delay tolerance exercises incremental code path // Covers: GraphDelayCalc::incrementalDelayTolerance TEST_F(DesignDcalcTest, IncrementalDelayToleranceQuery) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); GraphDelayCalc *gdc = sta_->graphDelayCalc(); float tol = gdc->incrementalDelayTolerance(); EXPECT_GE(tol, 0.0f); gdc->setIncrementalDelayTolerance(0.01f); EXPECT_FLOAT_EQ(gdc->incrementalDelayTolerance(), 0.01f); sta_->updateTiming(true); sta_->updateTiming(false); SUCCEED(); } // R10_ DesignDcalcTest: delayInvalid(Vertex*) and delayInvalid(Pin*) // Covers: GraphDelayCalc::delayInvalid variants TEST_F(DesignDcalcTest, DelayInvalidVariants) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Graph *graph = sta_->graph(); GraphDelayCalc *gdc = sta_->graphDelayCalc(); Instance *u1 = network->findChild(top, "u1"); if (u1) { Pin *y_pin = network->findPin(u1, "Y"); if (y_pin) { Vertex *v = graph->pinDrvrVertex(y_pin); if (v) { gdc->delayInvalid(v); } gdc->delayInvalid(y_pin); } } } // R10_ DesignDcalcTest: CCS ceff with actual parasitics // Covers: CcsCeffDelayCalc paths TEST_F(DesignDcalcTest, CcsCeffWithParasitics) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("ccs_ceff"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); } // R10_ DesignDcalcTest: CCS ceff with unreduced parasitics TEST_F(DesignDcalcTest, CcsCeffUnreducedParasitics) { ASSERT_TRUE(design_loaded_); Corner *corner = sta_->cmdCorner(); Instance *top = sta_->network()->topInstance(); sta_->readSpef("test/reg1_asap7.spef", top, corner, MinMaxAll::all(), false, false, 1.0f, false); sta_->setArcDelayCalc("ccs_ceff"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); } // R10_ DesignDcalcTest: prima with timing and reporting // Covers: PrimaDelayCalc internal methods TEST_F(DesignDcalcTest, PrimaTimingWithReport) { ASSERT_TRUE(design_loaded_); Corner *corner = sta_->cmdCorner(); Instance *top = sta_->network()->topInstance(); sta_->readSpef("test/reg1_asap7.spef", top, corner, MinMaxAll::all(), false, false, 1.0f, false); sta_->setArcDelayCalc("prima"); sta_->updateTiming(true); Network *network = sta_->network(); Graph *graph = sta_->graph(); GraphDelayCalc *gdc = sta_->graphDelayCalc(); ASSERT_NE(gdc, nullptr); Instance *u1 = network->findChild(top, "u1"); if (u1) { Pin *y_pin = network->findPin(u1, "Y"); if (y_pin) { Vertex *drv = graph->pinDrvrVertex(y_pin); if (drv) { VertexInEdgeIterator edge_iter(drv, graph); if (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); TimingArcSet *arc_set = edge->timingArcSet(); if (arc_set) { for (TimingArc *arc : arc_set->arcs()) { std::string report = gdc->reportDelayCalc(edge, arc, corner, MinMax::max(), 4); (void)report; break; } } } } } } } // R10_ DesignDcalcTest: bidirectDrvrSlewFromLoad // Covers: GraphDelayCalc::bidirectDrvrSlewFromLoad TEST_F(DesignDcalcTest, BidirectDrvrSlewFromLoad) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); GraphDelayCalc *gdc = sta_->graphDelayCalc(); Instance *u1 = network->findChild(top, "u1"); if (u1) { Pin *y_pin = network->findPin(u1, "Y"); if (y_pin) { bool from_load = gdc->bidirectDrvrSlewFromLoad(y_pin); EXPECT_FALSE(from_load); } } } // R10_ DesignDcalcTest: minPeriod query // Covers: GraphDelayCalc::minPeriod TEST_F(DesignDcalcTest, MinPeriodQuery) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Corner *corner = sta_->cmdCorner(); GraphDelayCalc *gdc = sta_->graphDelayCalc(); Pin *clk1 = network->findPin(top, "clk1"); if (clk1) { float min_period; bool exists; gdc->minPeriod(clk1, corner, min_period, exists); (void)min_period; (void)exists; } } // R10_ DesignDcalcTest: Arnoldi with loadCap and netCaps query // Covers: ArnoldiDelayCalc paths, delay_work_alloc, rcmodel TEST_F(DesignDcalcTest, ArnoldiLoadCapAndNetCaps) { ASSERT_TRUE(design_loaded_); Corner *corner = sta_->cmdCorner(); Instance *top = sta_->network()->topInstance(); sta_->readSpef("test/reg1_asap7.spef", top, corner, MinMaxAll::all(), false, false, 1.0f, false); sta_->setArcDelayCalc("arnoldi"); sta_->updateTiming(true); Network *network = sta_->network(); GraphDelayCalc *gdc = sta_->graphDelayCalc(); DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); Instance *u1 = network->findChild(top, "u1"); if (u1) { Pin *y_pin = network->findPin(u1, "Y"); if (y_pin) { float cap = gdc->loadCap(y_pin, dcalc_ap); EXPECT_GE(cap, 0.0f); float pin_cap, wire_cap, fanout; bool has_set_load; gdc->netCaps(y_pin, RiseFall::rise(), dcalc_ap, pin_cap, wire_cap, fanout, has_set_load); EXPECT_GE(pin_cap + wire_cap, 0.0f); } } } // R10_ ArcDcalcArg: edge() accessor returns nullptr for default-constructed arg TEST_F(ArcDcalcArgTest, DefaultEdgeIsNull) { ArcDcalcArg arg; EXPECT_EQ(arg.edge(), nullptr); EXPECT_EQ(arg.arc(), nullptr); EXPECT_EQ(arg.inPin(), nullptr); EXPECT_EQ(arg.drvrPin(), nullptr); EXPECT_EQ(arg.parasitic(), nullptr); } // R10_ DesignDcalcTest: exercise findDelays(Level) triggering FindVertexDelays BFS // Covers: FindVertexDelays::FindVertexDelays, ~FindVertexDelays, copy TEST_F(DesignDcalcTest, FindDelaysLevel) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->ensureGraph(); sta_->findDelays(); SUCCEED(); } // R10_ DesignDcalcTest: ArcDcalcArg with actual design edge // Covers: ArcDcalcArg::inEdge, drvrVertex, drvrNet with real data TEST_F(DesignDcalcTest, ArcDcalcArgWithRealEdge) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Graph *graph = sta_->graph(); Instance *u2 = network->findChild(top, "u2"); if (u2) { Pin *y_pin = network->findPin(u2, "Y"); Pin *a_pin = network->findPin(u2, "A"); if (y_pin && a_pin) { Vertex *drv = graph->pinDrvrVertex(y_pin); if (drv) { VertexInEdgeIterator edge_iter(drv, graph); if (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); TimingArcSet *arc_set = edge->timingArcSet(); if (arc_set) { for (TimingArc *arc : arc_set->arcs()) { // Construct with real edge/arc data ArcDcalcArg arg(a_pin, y_pin, edge, arc, 0.0f); // inEdge should return a valid RiseFall const RiseFall *in_rf = arg.inEdge(); EXPECT_NE(in_rf, nullptr); // drvrVertex with graph Vertex *v = arg.drvrVertex(graph); EXPECT_NE(v, nullptr); // drvrNet with network const Net *net = arg.drvrNet(network); (void)net; break; // Just test one arc } } } } } } } // R10_ DesignDcalcTest: makeArcDcalcArg with instance names // Covers: makeArcDcalcArg TEST_F(DesignDcalcTest, MakeArcDcalcArgByName) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); // makeArcDcalcArg(inst_name, in_port, in_rf, drvr_port, drvr_rf, input_delay, sta) ArcDcalcArg arg = makeArcDcalcArg("u2", "A", "rise", "Y", "rise", "0.0", sta_); // May or may not find the arc, but should not crash (void)arg; } // R10_ DesignDcalcTest: DmpCeff with incremental invalidation and recompute // Covers: GraphDelayCalc incremental paths TEST_F(DesignDcalcTest, DmpCeffElmoreLevelBasedIncremental) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->setIncrementalDelayTolerance(0.005f); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Graph *graph = sta_->graph(); GraphDelayCalc *gdc = sta_->graphDelayCalc(); Instance *u1 = network->findChild(top, "u1"); if (u1) { Pin *y_pin = network->findPin(u1, "Y"); if (y_pin) { Vertex *v = graph->pinDrvrVertex(y_pin); if (v) { gdc->delayInvalid(v); sta_->updateTiming(false); } } } } // R10_ DesignDcalcTest: Arnoldi reduce all driver nets // Covers: ArnoldiDelayCalc::reduce paths, delay_work_alloc TEST_F(DesignDcalcTest, ArnoldiReduceAllNets) { ASSERT_TRUE(design_loaded_); Corner *corner = sta_->cmdCorner(); Instance *top = sta_->network()->topInstance(); sta_->readSpef("test/reg1_asap7.spef", top, corner, MinMaxAll::all(), false, false, 1.0f, false); ArcDelayCalc *calc = makeDelayCalc("arnoldi", sta_); ASSERT_NE(calc, nullptr); Network *network = sta_->network(); InstanceChildIterator *child_iter = network->childIterator(top); int reduced_count = 0; while (child_iter->hasNext()) { Instance *inst = child_iter->next(); InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); if (network->direction(pin)->isAnyOutput()) { const MinMax *mm = MinMax::max(); DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); const Net *net = network->net(pin); if (net) { Parasitics *parasitics = sta_->parasitics(); ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm); Parasitic *pnet = parasitics->findParasiticNetwork(net, ap); if (pnet) { Parasitic *reduced = calc->reduceParasitic(pnet, pin, RiseFall::rise(), dcalc_ap); if (reduced) reduced_count++; } } } } delete pin_iter; } delete child_iter; delete calc; EXPECT_GE(reduced_count, 0); } // R10_ DesignDcalcTest: levelChangedBefore exercises vertex level change // Covers: GraphDelayCalc::levelChangedBefore TEST_F(DesignDcalcTest, LevelChangedBefore) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Graph *graph = sta_->graph(); GraphDelayCalc *gdc = sta_->graphDelayCalc(); Instance *u1 = network->findChild(top, "u1"); if (u1) { Pin *a_pin = network->findPin(u1, "A"); if (a_pin) { Vertex *v = graph->pinLoadVertex(a_pin); if (v) { gdc->levelChangedBefore(v); } } } } //////////////////////////////////////////////////////////////// // NangateDcalcTest - Loads Nangate45 + dcalc_test1.v (BUF->INV->DFF chain) class NangateDcalcTest : public ::testing::Test { protected: void SetUp() override { interp_ = Tcl_CreateInterp(); initSta(); sta_ = new Sta; Sta::setSta(sta_); sta_->makeComponents(); ReportTcl *report = dynamic_cast(sta_->report()); if (report) report->setTclInterp(interp_); registerDelayCalcs(); Corner *corner = sta_->cmdCorner(); const MinMaxAll *min_max = MinMaxAll::all(); LibertyLibrary *lib = sta_->readLiberty( "test/nangate45/Nangate45_typ.lib", corner, min_max, false); ASSERT_NE(lib, nullptr); bool ok = sta_->readVerilog("dcalc/test/dcalc_test1.v"); ASSERT_TRUE(ok); ok = sta_->linkDesign("dcalc_test1", true); ASSERT_TRUE(ok); // Create clock and set constraints Network *network = sta_->network(); Instance *top = network->topInstance(); Pin *clk_pin = network->findPin(top, "clk"); ASSERT_NE(clk_pin, nullptr); PinSet *clk_pins = new PinSet(network); clk_pins->insert(clk_pin); FloatSeq *waveform = new FloatSeq; waveform->push_back(0.0f); waveform->push_back(5.0f); sta_->makeClock("clk", clk_pins, false, 10.0f, waveform, nullptr); // Set input/output delay constraints to create constrained timing paths Clock *clk = sta_->sdc()->findClock("clk"); ASSERT_NE(clk, nullptr); Pin *in1_pin = network->findPin(top, "in1"); ASSERT_NE(in1_pin, nullptr); sta_->setInputDelay(in1_pin, RiseFallBoth::riseFall(), clk, RiseFall::rise(), nullptr, false, false, MinMaxAll::all(), false, 0.0f); Pin *out1_pin = network->findPin(top, "out1"); ASSERT_NE(out1_pin, nullptr); sta_->setOutputDelay(out1_pin, RiseFallBoth::riseFall(), clk, RiseFall::rise(), nullptr, false, false, MinMaxAll::all(), false, 0.0f); design_loaded_ = true; } void TearDown() override { deleteDelayCalcs(); deleteAllMemory(); sta_ = nullptr; if (interp_) Tcl_DeleteInterp(interp_); interp_ = nullptr; } Sta *sta_; Tcl_Interp *interp_; bool design_loaded_ = false; }; // Run updateTiming with each calculator, verify all complete without crash // and graph has delays. TEST_F(NangateDcalcTest, TimingAllCalcsNangate) { EXPECT_TRUE(design_loaded_); const char *calcs[] = {"unit", "lumped_cap", "dmp_ceff_elmore", "dmp_ceff_two_pole", "ccs_ceff"}; for (const char *name : calcs) { sta_->setArcDelayCalc(name); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); } } // Set various loads on output, run dmp_ceff_elmore for each, verify slack changes. TEST_F(NangateDcalcTest, DmpExtremeLoads) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); Network *network = sta_->network(); Instance *top = network->topInstance(); Cell *top_cell = network->cell(top); const Port *out_port = network->findPort(top_cell, "out1"); ASSERT_NE(out_port, nullptr); Corner *corner = sta_->cmdCorner(); float loads[] = {0.00001f, 0.1f, 1.0f, 5.0f, 10.0f}; Slack prev_slack = 0.0f; bool first = true; for (float load : loads) { sta_->setPortExtPinCap(out_port, RiseFallBoth::riseFall(), corner, MinMaxAll::all(), load); sta_->updateTiming(true); Slack slack = sta_->worstSlack(MinMax::max()); if (!first) { // With increasing load, slack should generally decrease (become worse) // but we just verify it's a valid number and changes EXPECT_TRUE(slack != prev_slack || load == loads[0]); } prev_slack = slack; first = false; } } // Set various input transitions via setInputSlew, verify timing completes. TEST_F(NangateDcalcTest, DmpExtremeSlews) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); Network *network = sta_->network(); Instance *top = network->topInstance(); Cell *top_cell = network->cell(top); const Port *in_port = network->findPort(top_cell, "in1"); ASSERT_NE(in_port, nullptr); float slews[] = {0.0001f, 0.1f, 5.0f, 10.0f}; for (float slew : slews) { sta_->setInputSlew(in_port, RiseFallBoth::riseFall(), MinMaxAll::all(), slew); sta_->updateTiming(true); SUCCEED(); } } // Large load + fast slew, tiny load + slow slew combinations. TEST_F(NangateDcalcTest, DmpCombinedExtremes) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); Network *network = sta_->network(); Instance *top = network->topInstance(); Cell *top_cell = network->cell(top); const Port *out_port = network->findPort(top_cell, "out1"); const Port *in_port = network->findPort(top_cell, "in1"); ASSERT_NE(out_port, nullptr); ASSERT_NE(in_port, nullptr); Corner *corner = sta_->cmdCorner(); // Large load + fast slew sta_->setPortExtPinCap(out_port, RiseFallBoth::riseFall(), corner, MinMaxAll::all(), 10.0f); sta_->setInputSlew(in_port, RiseFallBoth::riseFall(), MinMaxAll::all(), 0.0001f); sta_->updateTiming(true); Slack slack1 = sta_->worstSlack(MinMax::max()); // Tiny load + slow slew sta_->setPortExtPinCap(out_port, RiseFallBoth::riseFall(), corner, MinMaxAll::all(), 0.00001f); sta_->setInputSlew(in_port, RiseFallBoth::riseFall(), MinMaxAll::all(), 10.0f); sta_->updateTiming(true); Slack slack2 = sta_->worstSlack(MinMax::max()); // Just verify both complete and produce different slacks EXPECT_NE(slack1, slack2); } // Same as DmpExtremeLoads but with dmp_ceff_two_pole. TEST_F(NangateDcalcTest, TwoPoleExtremeLoads) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_two_pole"); Network *network = sta_->network(); Instance *top = network->topInstance(); Cell *top_cell = network->cell(top); const Port *out_port = network->findPort(top_cell, "out1"); ASSERT_NE(out_port, nullptr); Corner *corner = sta_->cmdCorner(); float loads[] = {0.00001f, 0.1f, 1.0f, 5.0f, 10.0f}; for (float load : loads) { sta_->setPortExtPinCap(out_port, RiseFallBoth::riseFall(), corner, MinMaxAll::all(), load); sta_->updateTiming(true); SUCCEED(); } } // Switch calculator from dmp_ceff_elmore->lumped_cap->unit->dmp_ceff_two_pole, // verify timing works at each switch. TEST_F(NangateDcalcTest, CalcSwitchingIncremental) { EXPECT_TRUE(design_loaded_); const char *calcs[] = {"dmp_ceff_elmore", "lumped_cap", "unit", "dmp_ceff_two_pole"}; for (const char *name : calcs) { sta_->setArcDelayCalc(name); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); } } // Set ccs_ceff (falls back to table-based for NLDM), verify timing works. TEST_F(NangateDcalcTest, CcsWithNldmFallback) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("ccs_ceff"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); Slack slack = sta_->worstSlack(MinMax::max()); // CCS with NLDM fallback should still produce valid timing (void)slack; SUCCEED(); } // Set ccs_ceff, change load, verify incremental timing. TEST_F(NangateDcalcTest, CcsIncrementalLoadChange) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("ccs_ceff"); sta_->updateTiming(true); Slack slack1 = sta_->worstSlack(MinMax::max()); Network *network = sta_->network(); Instance *top = network->topInstance(); Cell *top_cell = network->cell(top); const Port *out_port = network->findPort(top_cell, "out1"); ASSERT_NE(out_port, nullptr); Corner *corner = sta_->cmdCorner(); sta_->setPortExtPinCap(out_port, RiseFallBoth::riseFall(), corner, MinMaxAll::all(), 5.0f); sta_->updateTiming(false); Slack slack2 = sta_->worstSlack(MinMax::max()); // With large load, slack should change EXPECT_NE(slack1, slack2); } //////////////////////////////////////////////////////////////// // MultiDriverDcalcTest - Loads Nangate45 + dcalc_multidriver_test.v class MultiDriverDcalcTest : public ::testing::Test { protected: void SetUp() override { interp_ = Tcl_CreateInterp(); initSta(); sta_ = new Sta; Sta::setSta(sta_); sta_->makeComponents(); ReportTcl *report = dynamic_cast(sta_->report()); if (report) report->setTclInterp(interp_); registerDelayCalcs(); Corner *corner = sta_->cmdCorner(); const MinMaxAll *min_max = MinMaxAll::all(); LibertyLibrary *lib = sta_->readLiberty( "test/nangate45/Nangate45_typ.lib", corner, min_max, false); ASSERT_NE(lib, nullptr); bool ok = sta_->readVerilog("dcalc/test/dcalc_multidriver_test.v"); ASSERT_TRUE(ok); ok = sta_->linkDesign("dcalc_multidriver_test", true); ASSERT_TRUE(ok); // Create clock Network *network = sta_->network(); Instance *top = network->topInstance(); Pin *clk_pin = network->findPin(top, "clk"); ASSERT_NE(clk_pin, nullptr); PinSet *clk_pins = new PinSet(network); clk_pins->insert(clk_pin); FloatSeq *waveform = new FloatSeq; waveform->push_back(0.0f); waveform->push_back(5.0f); sta_->makeClock("clk", clk_pins, false, 10.0f, waveform, nullptr); Clock *clk = sta_->sdc()->findClock("clk"); ASSERT_NE(clk, nullptr); // Set input delays on in1-in4, sel Cell *top_cell = network->cell(top); const char *input_ports[] = {"in1", "in2", "in3", "in4", "sel"}; for (const char *pname : input_ports) { const Port *port = network->findPort(top_cell, pname); if (port) { sta_->setInputSlew(port, RiseFallBoth::riseFall(), MinMaxAll::all(), 0.1f); // Also set SDC input delay to constrain the path Pin *pin = network->findPin(top, pname); if (pin) { sta_->setInputDelay(pin, RiseFallBoth::riseFall(), clk, RiseFall::rise(), nullptr, false, false, MinMaxAll::all(), false, 0.0f); } } } // Set output loads and output delays on out1-out3 const char *output_ports[] = {"out1", "out2", "out3"}; for (const char *pname : output_ports) { const Port *port = network->findPort(top_cell, pname); if (port) { sta_->setPortExtPinCap(port, RiseFallBoth::riseFall(), corner, MinMaxAll::all(), 0.01f); Pin *pin = network->findPin(top, pname); if (pin) { sta_->setOutputDelay(pin, RiseFallBoth::riseFall(), clk, RiseFall::rise(), nullptr, false, false, MinMaxAll::all(), false, 0.0f); } } } design_loaded_ = true; } void TearDown() override { deleteDelayCalcs(); deleteAllMemory(); sta_ = nullptr; if (interp_) Tcl_DeleteInterp(interp_); interp_ = nullptr; } Sta *sta_; Tcl_Interp *interp_; bool design_loaded_ = false; }; // updateTiming, query paths from each input to each output, verify graph has paths. TEST_F(MultiDriverDcalcTest, AllPathQueries) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 10); Network *network = sta_->network(); Instance *top = network->topInstance(); // Verify output pins have vertices const char *out_names[] = {"out1", "out2", "out3"}; for (const char *name : out_names) { Pin *pin = network->findPin(top, name); ASSERT_NE(pin, nullptr); Vertex *v = graph->pinDrvrVertex(pin); EXPECT_NE(v, nullptr); } } // Sweep loads 0.001->0.1 on out1, verify delays change monotonically. TEST_F(MultiDriverDcalcTest, DmpCeffLoadSweep) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); Network *network = sta_->network(); Instance *top = network->topInstance(); Cell *top_cell = network->cell(top); const Port *out_port = network->findPort(top_cell, "out1"); ASSERT_NE(out_port, nullptr); Corner *corner = sta_->cmdCorner(); float loads[] = {0.001f, 0.005f, 0.01f, 0.05f, 0.1f}; Slack prev_slack = 1e30f; // Start with large positive value for (float load : loads) { sta_->setPortExtPinCap(out_port, RiseFallBoth::riseFall(), corner, MinMaxAll::all(), load); sta_->updateTiming(true); Slack slack = sta_->worstSlack(MinMax::max()); // With increasing load, slack should decrease (more negative = worse) EXPECT_LE(slack, prev_slack + 1e-6f); prev_slack = slack; } } // Set large tolerance (0.5), change slew, verify timing completes. TEST_F(MultiDriverDcalcTest, IncrementalToleranceLarge) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->setIncrementalDelayTolerance(0.5f); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Cell *top_cell = network->cell(top); const Port *in_port = network->findPort(top_cell, "in1"); ASSERT_NE(in_port, nullptr); sta_->setInputSlew(in_port, RiseFallBoth::riseFall(), MinMaxAll::all(), 0.5f); sta_->updateTiming(false); SUCCEED(); } // Set small tolerance (0.001), change slew, verify timing recomputes. TEST_F(MultiDriverDcalcTest, IncrementalToleranceSmall) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->setIncrementalDelayTolerance(0.001f); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Cell *top_cell = network->cell(top); const Port *in_port = network->findPort(top_cell, "in1"); ASSERT_NE(in_port, nullptr); sta_->setInputSlew(in_port, RiseFallBoth::riseFall(), MinMaxAll::all(), 0.5f); sta_->updateTiming(false); SUCCEED(); } // Set loads on multiple outputs, verify incremental update works. TEST_F(MultiDriverDcalcTest, IncrementalLoadChanges) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Cell *top_cell = network->cell(top); Corner *corner = sta_->cmdCorner(); const char *output_ports[] = {"out1", "out2", "out3"}; for (const char *pname : output_ports) { const Port *port = network->findPort(top_cell, pname); ASSERT_NE(port, nullptr); sta_->setPortExtPinCap(port, RiseFallBoth::riseFall(), corner, MinMaxAll::all(), 1.0f); } sta_->updateTiming(false); Slack slack = sta_->worstSlack(MinMax::max()); (void)slack; SUCCEED(); } // Change clock period, verify timing updates. TEST_F(MultiDriverDcalcTest, IncrementalClockPeriodChange) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Slack slack1 = sta_->worstSlack(MinMax::max()); // Create new clock with different period Network *network = sta_->network(); Instance *top = network->topInstance(); Pin *clk_pin = network->findPin(top, "clk"); ASSERT_NE(clk_pin, nullptr); PinSet *clk_pins = new PinSet(network); clk_pins->insert(clk_pin); FloatSeq *waveform = new FloatSeq; waveform->push_back(0.0f); waveform->push_back(1.0f); sta_->makeClock("clk", clk_pins, false, 2.0f, waveform, nullptr); sta_->updateTiming(true); Slack slack2 = sta_->worstSlack(MinMax::max()); // Tighter clock => smaller (worse) slack EXPECT_NE(slack1, slack2); } // Replace buf1 with BUF_X4, verify timing completes, replace back. TEST_F(MultiDriverDcalcTest, ReplaceCellIncremental) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Network *network = sta_->network(); Instance *top = network->topInstance(); Instance *buf1 = network->findChild(top, "buf1"); ASSERT_NE(buf1, nullptr); LibertyCell *buf_x4 = network->findLibertyCell("BUF_X4"); ASSERT_NE(buf_x4, nullptr); LibertyCell *buf_x1 = network->findLibertyCell("BUF_X1"); ASSERT_NE(buf_x1, nullptr); // Check vertex delay on buf1 output before replacement Graph *graph = sta_->graph(); Pin *buf1_z = network->findPin(buf1, "Z"); ASSERT_NE(buf1_z, nullptr); Vertex *v1 = graph->pinDrvrVertex(buf1_z); ASSERT_NE(v1, nullptr); sta_->replaceCell(buf1, buf_x4); sta_->updateTiming(true); // Verify timing completes after replacement graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); // Replace back to original sta_->replaceCell(buf1, buf_x1); sta_->updateTiming(true); graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); SUCCEED(); } // Switch through all 5 calculators, verify timing at each. TEST_F(MultiDriverDcalcTest, CalcSwitchAllEngines) { EXPECT_TRUE(design_loaded_); const char *calcs[] = {"unit", "lumped_cap", "dmp_ceff_elmore", "dmp_ceff_two_pole", "ccs_ceff"}; for (const char *name : calcs) { sta_->setArcDelayCalc(name); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); } } // Call findDelays() directly, invalidate, call again. TEST_F(MultiDriverDcalcTest, FindDelaysExplicit) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->findDelays(); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); // Change something and call findDelays again Network *network = sta_->network(); Instance *top = network->topInstance(); Cell *top_cell = network->cell(top); const Port *in_port = network->findPort(top_cell, "in1"); ASSERT_NE(in_port, nullptr); sta_->setInputSlew(in_port, RiseFallBoth::riseFall(), MinMaxAll::all(), 1.0f); sta_->findDelays(); SUCCEED(); } //////////////////////////////////////////////////////////////// // MultiCornerDcalcTest - Loads Nangate45 fast/slow + dcalc_test1.v class MultiCornerDcalcTest : public ::testing::Test { protected: void SetUp() override { interp_ = Tcl_CreateInterp(); initSta(); sta_ = new Sta; Sta::setSta(sta_); sta_->makeComponents(); ReportTcl *report = dynamic_cast(sta_->report()); if (report) report->setTclInterp(interp_); registerDelayCalcs(); // Define corners StringSet corner_names; corner_names.insert("fast"); corner_names.insert("slow"); sta_->makeCorners(&corner_names); Corner *fast_corner = sta_->findCorner("fast"); Corner *slow_corner = sta_->findCorner("slow"); ASSERT_NE(fast_corner, nullptr); ASSERT_NE(slow_corner, nullptr); const MinMaxAll *min_max = MinMaxAll::all(); LibertyLibrary *fast_lib = sta_->readLiberty( "test/nangate45/Nangate45_fast.lib", fast_corner, min_max, false); ASSERT_NE(fast_lib, nullptr); LibertyLibrary *slow_lib = sta_->readLiberty( "test/nangate45/Nangate45_slow.lib", slow_corner, min_max, false); ASSERT_NE(slow_lib, nullptr); bool ok = sta_->readVerilog("dcalc/test/dcalc_test1.v"); ASSERT_TRUE(ok); ok = sta_->linkDesign("dcalc_test1", true); ASSERT_TRUE(ok); // Create clock Network *network = sta_->network(); Instance *top = network->topInstance(); Pin *clk_pin = network->findPin(top, "clk"); ASSERT_NE(clk_pin, nullptr); PinSet *clk_pins = new PinSet(network); clk_pins->insert(clk_pin); FloatSeq *waveform = new FloatSeq; waveform->push_back(0.0f); waveform->push_back(5.0f); sta_->makeClock("clk", clk_pins, false, 10.0f, waveform, nullptr); // Set input/output delay constraints to create constrained timing paths Clock *clk = sta_->sdc()->findClock("clk"); ASSERT_NE(clk, nullptr); Pin *in1_pin = network->findPin(top, "in1"); ASSERT_NE(in1_pin, nullptr); sta_->setInputDelay(in1_pin, RiseFallBoth::riseFall(), clk, RiseFall::rise(), nullptr, false, false, MinMaxAll::all(), false, 0.0f); Pin *out1_pin = network->findPin(top, "out1"); ASSERT_NE(out1_pin, nullptr); sta_->setOutputDelay(out1_pin, RiseFallBoth::riseFall(), clk, RiseFall::rise(), nullptr, false, false, MinMaxAll::all(), false, 0.0f); design_loaded_ = true; } void TearDown() override { deleteDelayCalcs(); deleteAllMemory(); sta_ = nullptr; if (interp_) Tcl_DeleteInterp(interp_); interp_ = nullptr; } Sta *sta_; Tcl_Interp *interp_; bool design_loaded_ = false; }; // Verify timing with both corners produces valid results and // that the slow corner does not have better slack than the fast corner. TEST_F(MultiCornerDcalcTest, TimingDiffersPerCorner) { EXPECT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Corner *fast_corner = sta_->findCorner("fast"); Corner *slow_corner = sta_->findCorner("slow"); ASSERT_NE(fast_corner, nullptr); ASSERT_NE(slow_corner, nullptr); Slack fast_slack, slow_slack; Vertex *fast_vertex, *slow_vertex; sta_->worstSlack(fast_corner, MinMax::max(), fast_slack, fast_vertex); sta_->worstSlack(slow_corner, MinMax::max(), slow_slack, slow_vertex); // Both corners should produce valid slack (not infinity) EXPECT_LT(fast_slack, 1e29f); EXPECT_LT(slow_slack, 1e29f); // Fast corner should have slack >= slow corner (better or equal) EXPECT_GE(fast_slack, slow_slack); } // Run each calculator with multi-corner, verify completes. TEST_F(MultiCornerDcalcTest, AllCalcsMultiCorner) { EXPECT_TRUE(design_loaded_); const char *calcs[] = {"unit", "lumped_cap", "dmp_ceff_elmore", "dmp_ceff_two_pole", "ccs_ceff"}; for (const char *name : calcs) { sta_->setArcDelayCalc(name); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); } } //////////////////////////////////////////////////////////////// // Additional DesignDcalcTest tests for SPEF-based scenarios // Run all delay calculators with SPEF loaded. TEST_F(DesignDcalcTest, TimingAllCalcsWithSpef) { ASSERT_TRUE(design_loaded_); const char *calcs[] = {"unit", "lumped_cap", "dmp_ceff_elmore", "dmp_ceff_two_pole", "arnoldi", "ccs_ceff", "prima"}; for (const char *name : calcs) { sta_->setArcDelayCalc(name); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); } } // Set prima reduce order 1,2,3,4,5, verify each completes. TEST_F(DesignDcalcTest, PrimaReduceOrderVariation) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("prima"); ArcDelayCalc *calc = sta_->arcDelayCalc(); ASSERT_NE(calc, nullptr); PrimaDelayCalc *prima = dynamic_cast(calc); ASSERT_NE(prima, nullptr); size_t orders[] = {1, 2, 3, 4, 5}; for (size_t order : orders) { prima->setPrimaReduceOrder(order); sta_->updateTiming(true); SUCCEED(); } } // Change load, slew, clock period with SPEF, verify updates. TEST_F(DesignDcalcTest, IncrementalWithSpef) { ASSERT_TRUE(design_loaded_); sta_->setArcDelayCalc("dmp_ceff_elmore"); sta_->updateTiming(true); Slack slack1 = sta_->worstSlack(MinMax::max()); // Change clock period Network *network = sta_->network(); Instance *top = network->topInstance(); Pin *clk1 = network->findPin(top, "clk1"); Pin *clk2 = network->findPin(top, "clk2"); Pin *clk3 = network->findPin(top, "clk3"); PinSet *clk_pins = new PinSet(network); clk_pins->insert(clk1); clk_pins->insert(clk2); clk_pins->insert(clk3); FloatSeq *waveform = new FloatSeq; waveform->push_back(0.0f); waveform->push_back(50.0f); sta_->makeClock("clk", clk_pins, false, 100.0f, waveform, nullptr); sta_->updateTiming(true); Slack slack2 = sta_->worstSlack(MinMax::max()); // Tighter clock => different slack EXPECT_NE(slack1, slack2); } // Rapidly switch between all calcs with SPEF loaded. TEST_F(DesignDcalcTest, RapidCalcSwitchingSpef) { ASSERT_TRUE(design_loaded_); const char *calcs[] = {"dmp_ceff_elmore", "lumped_cap", "unit", "dmp_ceff_two_pole", "arnoldi", "ccs_ceff", "prima", "dmp_ceff_elmore", "ccs_ceff"}; for (const char *name : calcs) { sta_->setArcDelayCalc(name); sta_->updateTiming(true); Graph *graph = sta_->graph(); ASSERT_NE(graph, nullptr); EXPECT_GT(graph->vertexCount(), 0); } } } // namespace sta