#include #include #include #include #include #include "Units.hh" #include "TimingRole.hh" #include "MinMax.hh" #include "Wireload.hh" #include "FuncExpr.hh" #include "TableModel.hh" #include "TimingArc.hh" #include "Liberty.hh" #include "InternalPower.hh" #include "LinearModel.hh" #include "Transition.hh" #include "RiseFallValues.hh" #include "PortDirection.hh" #include "StringUtil.hh" #include "liberty/LibertyParser.hh" #include "liberty/LibertyBuilder.hh" #include "ReportStd.hh" #include "liberty/LibertyReaderPvt.hh" #include #include "Sta.hh" #include "ReportTcl.hh" #include "PatternMatch.hh" #include "Scene.hh" #include "LibertyWriter.hh" namespace sta { static void expectStaLibertyCoreState(Sta *sta, LibertyLibrary *lib) { ASSERT_NE(sta, nullptr); EXPECT_EQ(Sta::sta(), sta); EXPECT_NE(sta->network(), nullptr); EXPECT_NE(sta->search(), nullptr); EXPECT_NE(sta->cmdSdc(), nullptr); EXPECT_NE(sta->report(), nullptr); EXPECT_FALSE(sta->scenes().empty()); if (!sta->scenes().empty()) EXPECT_GE(sta->scenes().size(), 1); EXPECT_NE(sta->cmdScene(), nullptr); EXPECT_NE(lib, nullptr); } // Lightweight fixture classes needed by R5_ tests in this file class UnitTest : public ::testing::Test { protected: void SetUp() override {} }; class Table1Test : public ::testing::Test { protected: TableAxisPtr makeAxis(std::initializer_list vals) { FloatSeq values; for (float v : vals) values.push_back(v); return std::make_shared( TableAxisVariable::total_output_net_capacitance, std::move(values)); } }; class LinearModelTest : public ::testing::Test { protected: void SetUp() override { lib_ = new LibertyLibrary("test_lib", "test.lib"); cell_ = new LibertyCell(lib_, "INV", "inv.lib"); } void TearDown() override { delete cell_; delete lib_; } LibertyLibrary *lib_; LibertyCell *cell_; }; class StaLibertyTest : 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_); // Read Nangate45 liberty file lib_ = sta_->readLiberty("test/nangate45/Nangate45_typ.lib", sta_->cmdScene(), MinMaxAll::min(), false); } void TearDown() override { if (sta_) expectStaLibertyCoreState(sta_, lib_); deleteAllMemory(); sta_ = nullptr; if (interp_) Tcl_DeleteInterp(interp_); interp_ = nullptr; } Sta *sta_; Tcl_Interp *interp_; LibertyLibrary *lib_; }; TEST_F(StaLibertyTest, LibraryNotNull) { EXPECT_NE(lib_, nullptr); } TEST_F(StaLibertyTest, FindLibertyCell) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); EXPECT_NE(buf, nullptr); LibertyCell *inv = lib_->findLibertyCell("INV_X1"); EXPECT_NE(inv, nullptr); EXPECT_EQ(lib_->findLibertyCell("NONEXISTENT_CELL_XYZ"), nullptr); } TEST_F(StaLibertyTest, FindLibertyCellsMatching) { PatternMatch pattern("BUF_*", false, false, nullptr); auto cells = lib_->findLibertyCellsMatching(&pattern); EXPECT_GT(cells.size(), 0u); } TEST_F(StaLibertyTest, LibraryCellIterator) { LibertyCellIterator iter(lib_); int count = 0; while (iter.hasNext()) { LibertyCell *cell = iter.next(); EXPECT_NE(cell, nullptr); count++; } EXPECT_GT(count, 0); } TEST_F(StaLibertyTest, CellArea) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); float area = buf->area(); EXPECT_GT(area, 0.0f); } TEST_F(StaLibertyTest, CellIsBuffer) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_TRUE(buf->isBuffer()); } TEST_F(StaLibertyTest, CellIsInverter) { LibertyCell *inv = lib_->findLibertyCell("INV_X1"); ASSERT_NE(inv, nullptr); EXPECT_TRUE(inv->isInverter()); } TEST_F(StaLibertyTest, CellBufferPorts) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_TRUE(buf->isBuffer()); LibertyPort *input = nullptr; LibertyPort *output = nullptr; buf->bufferPorts(input, output); EXPECT_NE(input, nullptr); EXPECT_NE(output, nullptr); } TEST_F(StaLibertyTest, CellHasTimingArcs) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); EXPECT_TRUE(buf->hasTimingArcs(a)); } TEST_F(StaLibertyTest, CellFindLibertyPort) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); EXPECT_NE(a, nullptr); LibertyPort *z = buf->findLibertyPort("Z"); EXPECT_NE(z, nullptr); EXPECT_EQ(buf->findLibertyPort("NONEXISTENT_PORT"), nullptr); } TEST_F(StaLibertyTest, CellTimingArcSets) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); EXPECT_GT(arcsets.size(), 0u); EXPECT_GT(buf->timingArcSetCount(), 0u); } TEST_F(StaLibertyTest, CellTimingArcSetsFromTo) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); LibertyPort *z = buf->findLibertyPort("Z"); ASSERT_NE(a, nullptr); ASSERT_NE(z, nullptr); auto &arcsets = buf->timingArcSets(a, z); EXPECT_GT(arcsets.size(), 0u); } TEST_F(StaLibertyTest, TimingArcSetProperties) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *arcset = arcsets[0]; EXPECT_NE(arcset, nullptr); // Test arc set properties EXPECT_NE(arcset->from(), nullptr); EXPECT_NE(arcset->to(), nullptr); EXPECT_NE(arcset->role(), nullptr); EXPECT_FALSE(arcset->isWire()); TimingSense sense = arcset->sense(); EXPECT_GE(static_cast(sense), 0); EXPECT_GT(arcset->arcCount(), 0u); EXPECT_GE(arcset->index(), 0u); EXPECT_EQ(arcset->libertyCell(), buf); } TEST_F(StaLibertyTest, TimingArcSetIsRisingFallingEdge) { ASSERT_NO_THROW(( [&](){ LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); if (dff) { auto &arcsets = dff->timingArcSets(); for (auto *arcset : arcsets) { // isRisingFallingEdge returns nullptr for combinational arcs const RiseFall *rf = arcset->isRisingFallingEdge(); if (rf) { EXPECT_TRUE(rf == RiseFall::rise() || rf == RiseFall::fall()); } } } }() )); } TEST_F(StaLibertyTest, TimingArcSetArcsFrom) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *arcset = arcsets[0]; TimingArc *arc1 = nullptr; TimingArc *arc2 = nullptr; arcset->arcsFrom(RiseFall::rise(), arc1, arc2); // At least one arc should exist EXPECT_TRUE(arc1 != nullptr || arc2 != nullptr); } TEST_F(StaLibertyTest, TimingArcSetArcTo) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *arcset = arcsets[0]; TimingArc *arc = arcset->arcTo(RiseFall::rise()); // May or may not be nullptr depending on the arc EXPECT_NE(arc, nullptr); } TEST_F(StaLibertyTest, TimingArcSetOcvArcDepth) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *arcset = arcsets[0]; float depth = arcset->ocvArcDepth(); EXPECT_GE(depth, 0.0f); } TEST_F(StaLibertyTest, TimingArcSetEquivAndLess) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); if (arcsets.size() >= 2) { TimingArcSet *set1 = arcsets[0]; TimingArcSet *set2 = arcsets[1]; // Test equiv - same set should be equiv EXPECT_TRUE(TimingArcSet::equiv(set1, set1)); // Test less - antisymmetric bool less12 = TimingArcSet::less(set1, set2); bool less21 = TimingArcSet::less(set2, set1); EXPECT_FALSE(less12 && less21); // Can't both be true } } TEST_F(StaLibertyTest, TimingArcSetCondDefault) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *arcset = arcsets[0]; // Just call the getter for coverage bool is_default = arcset->isCondDefault(); // is_default value depends on cell type } TEST_F(StaLibertyTest, TimingArcSetSdfCond) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *arcset = arcsets[0]; // SDF condition getters - may be empty for simple arcs const std::string &sdf_cond = arcset->sdfCond(); const std::string &sdf_start = arcset->sdfCondStart(); const std::string &sdf_end = arcset->sdfCondEnd(); const std::string &mode_name = arcset->modeName(); const std::string &mode_value = arcset->modeValue(); // sdf_cond may be empty for simple arcs // sdf_start may be empty for simple arcs // sdf_end may be empty for simple arcs // mode_name may be empty for simple arcs // mode_value may be empty for simple arcs } TEST_F(StaLibertyTest, TimingArcProperties) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *arcset = arcsets[0]; auto &arcs = arcset->arcs(); ASSERT_GT(arcs.size(), 0u); TimingArc *arc = arcs[0]; EXPECT_NE(arc->from(), nullptr); EXPECT_NE(arc->to(), nullptr); EXPECT_NE(arc->fromEdge(), nullptr); EXPECT_NE(arc->toEdge(), nullptr); EXPECT_NE(arc->role(), nullptr); EXPECT_EQ(arc->set(), arcset); EXPECT_GE(arc->index(), 0u); // Test sense TimingSense sense = arc->sense(); EXPECT_GE(static_cast(sense), 0); // Test to_string std::string arc_str = arc->to_string(); EXPECT_FALSE(arc_str.empty()); // Test model TimingModel *model = arc->model(); // model may be null depending on cell type } TEST_F(StaLibertyTest, TimingArcDriveResistance) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *arcset = arcsets[0]; auto &arcs = arcset->arcs(); ASSERT_GT(arcs.size(), 0u); TimingArc *arc = arcs[0]; float drive_res = arc->driveResistance(); EXPECT_GE(drive_res, 0.0f); } TEST_F(StaLibertyTest, TimingArcIntrinsicDelay) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *arcset = arcsets[0]; auto &arcs = arcset->arcs(); ASSERT_GT(arcs.size(), 0u); TimingArc *arc = arcs[0]; ArcDelay delay = arc->intrinsicDelay(); EXPECT_GE(delayAsFloat(delay), 0.0f); } TEST_F(StaLibertyTest, TimingArcEquiv) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); auto &arcs = arcsets[0]->arcs(); ASSERT_GT(arcs.size(), 0u); TimingArc *arc = arcs[0]; EXPECT_TRUE(TimingArc::equiv(arc, arc)); } TEST_F(StaLibertyTest, TimingArcGateTableModel) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); auto &arcs = arcsets[0]->arcs(); ASSERT_GT(arcs.size(), 0u); TimingArc *arc = arcs[0]; GateTableModel *gtm = arc->gateTableModel(); if (gtm) { EXPECT_NE(gtm->delayModel(), nullptr); } } TEST_F(StaLibertyTest, LibraryPortProperties) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); LibertyPort *z = buf->findLibertyPort("Z"); ASSERT_NE(a, nullptr); ASSERT_NE(z, nullptr); // Test capacitance getters float cap = a->capacitance(); EXPECT_GE(cap, 0.0f); float cap_min = a->capacitance(MinMax::min()); EXPECT_GE(cap_min, 0.0f); float cap_rise_max = a->capacitance(RiseFall::rise(), MinMax::max()); EXPECT_GE(cap_rise_max, 0.0f); // Test capacitance with exists float cap_val; bool exists; a->capacitance(RiseFall::rise(), MinMax::max(), cap_val, exists); // This may or may not exist depending on the lib // Test capacitanceIsOneValue bool one_val = a->capacitanceIsOneValue(); // one_val value depends on cell type // Test driveResistance float drive_res = z->driveResistance(); EXPECT_GE(drive_res, 0.0f); float drive_res_rise = z->driveResistance(RiseFall::rise(), MinMax::max()); EXPECT_GE(drive_res_rise, 0.0f); } TEST_F(StaLibertyTest, PortFunction) { LibertyCell *inv = lib_->findLibertyCell("INV_X1"); ASSERT_NE(inv, nullptr); LibertyPort *zn = inv->findLibertyPort("ZN"); ASSERT_NE(zn, nullptr); FuncExpr *func = zn->function(); EXPECT_NE(func, nullptr); } TEST_F(StaLibertyTest, PortTristateEnable) { // Find a tristate cell if available LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *z = buf->findLibertyPort("Z"); ASSERT_NE(z, nullptr); FuncExpr *tristate = z->tristateEnable(); // BUF_X1 does not have a tristate enable EXPECT_EQ(tristate, nullptr); } TEST_F(StaLibertyTest, PortClockFlags) { ASSERT_NO_THROW(( [&](){ LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); if (dff) { LibertyPort *ck = dff->findLibertyPort("CK"); if (ck) { bool is_clk = ck->isClock(); bool is_reg_clk = ck->isRegClk(); bool is_check_clk = ck->isCheckClk(); // is_clk tested implicitly (bool accessor exercised) // is_reg_clk tested implicitly (bool accessor exercised) // is_check_clk tested implicitly (bool accessor exercised) } LibertyPort *q = dff->findLibertyPort("Q"); if (q) { bool is_reg_out = q->isRegOutput(); // is_reg_out tested implicitly (bool accessor exercised) } } }() )); } TEST_F(StaLibertyTest, PortLimitGetters) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); float limit; bool exists; a->slewLimit(MinMax::max(), limit, exists); // May or may not exist if (exists) { EXPECT_GE(limit, 0.0f); } a->capacitanceLimit(MinMax::max(), limit, exists); if (exists) { EXPECT_GE(limit, 0.0f); } a->fanoutLimit(MinMax::max(), limit, exists); if (exists) { EXPECT_GE(limit, 0.0f); } float fanout_load; bool fl_exists; a->fanoutLoad(fanout_load, fl_exists); if (fl_exists) { EXPECT_GE(fanout_load, 0.0f); } } TEST_F(StaLibertyTest, PortMinPeriod) { ASSERT_NO_THROW(( [&](){ LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); if (dff) { LibertyPort *ck = dff->findLibertyPort("CK"); if (ck) { float min_period; bool exists; ck->minPeriod(min_period, exists); // May or may not exist if (exists) { EXPECT_GE(min_period, 0.0f); } } } }() )); } TEST_F(StaLibertyTest, PortMinPulseWidth) { ASSERT_NO_THROW(( [&](){ LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); if (dff) { LibertyPort *ck = dff->findLibertyPort("CK"); if (ck) { float min_width; bool exists; ck->minPulseWidth(RiseFall::rise(), min_width, exists); if (exists) { EXPECT_GE(min_width, 0.0f); } ck->minPulseWidth(RiseFall::fall(), min_width, exists); if (exists) { EXPECT_GE(min_width, 0.0f); } } } }() )); } TEST_F(StaLibertyTest, PortPwrGndProperties) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); // Regular ports are not power/ground EXPECT_FALSE(a->isPwrGnd()); EXPECT_EQ(a->pwrGndType(), PwrGndType::none); } TEST_F(StaLibertyTest, PortScanSignalType) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); // Regular ports have ScanSignalType::none EXPECT_EQ(a->scanSignalType(), ScanSignalType::none); } TEST_F(StaLibertyTest, PortBoolFlags) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); EXPECT_FALSE(a->isClockGateClock()); EXPECT_FALSE(a->isClockGateEnable()); EXPECT_FALSE(a->isClockGateOut()); EXPECT_FALSE(a->isPllFeedback()); EXPECT_FALSE(a->isolationCellData()); EXPECT_FALSE(a->isolationCellEnable()); EXPECT_FALSE(a->levelShifterData()); EXPECT_FALSE(a->isSwitch()); EXPECT_FALSE(a->isLatchData()); EXPECT_FALSE(a->isPad()); } TEST_F(StaLibertyTest, PortRelatedPorts) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); LibertyPort *ground_port = a->relatedGroundPort(); LibertyPort *power_port = a->relatedPowerPort(); // ground_port may be null for simple cells // power_port may be null for simple cells } TEST_F(StaLibertyTest, PortLibertyLibrary) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); EXPECT_EQ(a->libertyLibrary(), lib_); EXPECT_EQ(a->libertyCell(), buf); } TEST_F(StaLibertyTest, PortPulseClk) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); EXPECT_EQ(a->pulseClkTrigger(), nullptr); EXPECT_EQ(a->pulseClkSense(), nullptr); } TEST_F(StaLibertyTest, PortBusDcl) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); BusDcl *bus = a->busDcl(); EXPECT_EQ(bus, nullptr); // Scalar port has no bus declaration } TEST_F(StaLibertyTest, PortReceiverModel) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); const ReceiverModel *rm = a->receiverModel(); // rm may be null depending on cell type } TEST_F(StaLibertyTest, CellInternalPowers) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &powers = buf->internalPowers(); EXPECT_GT(powers.size(), 0u); if (powers.size() > 0) { const InternalPower &pwr = powers[0]; EXPECT_NE(pwr.port(), nullptr); // relatedPort may be nullptr LibertyPort *rp = pwr.relatedPort(); EXPECT_NE(rp, nullptr); // when is null for unconditional internal power groups FuncExpr *when = pwr.when(); if (when) { EXPECT_NE(when->op(), FuncExpr::Op::zero); } // relatedPgPin may be nullptr LibertyPort *pgpin = pwr.relatedPgPin(); // pgpin may be null for simple arcs EXPECT_EQ(pwr.libertyCell(), buf); } } TEST_F(StaLibertyTest, CellInternalPowersByPort) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *z = buf->findLibertyPort("Z"); if (z) { InternalPowerPtrSeq powers = buf->internalPowers(z); // May or may not have internal powers for this port EXPECT_GE(powers.size(), 0u); } } TEST_F(StaLibertyTest, CellDontUse) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); bool dont_use = buf->dontUse(); // dont_use value depends on cell type } TEST_F(StaLibertyTest, CellIsMacro) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isMacro()); } TEST_F(StaLibertyTest, CellIsMemory) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isMemory()); } TEST_F(StaLibertyTest, CellLibraryPtr) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_EQ(buf->libertyLibrary(), lib_); // Non-const version LibertyLibrary *lib_nc = buf->libertyLibrary(); EXPECT_EQ(lib_nc, lib_); } TEST_F(StaLibertyTest, CellFindLibertyPortsMatching) { LibertyCell *and2 = lib_->findLibertyCell("AND2_X1"); if (and2) { PatternMatch pattern("A*", false, false, nullptr); auto ports = and2->findLibertyPortsMatching(&pattern); EXPECT_GT(ports.size(), 0u); } } TEST_F(StaLibertyTest, LibraryCellPortIterator) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyCellPortIterator iter(buf); int count = 0; while (iter.hasNext()) { LibertyPort *port = iter.next(); EXPECT_NE(port, nullptr); count++; } EXPECT_GT(count, 0); } TEST_F(StaLibertyTest, LibertyCellPortBitIterator) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyCellPortBitIterator iter(buf); int count = 0; while (iter.hasNext()) { LibertyPort *port = iter.next(); EXPECT_NE(port, nullptr); count++; } EXPECT_GT(count, 0); } TEST_F(StaLibertyTest, LibertyPortMemberIterator) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); LibertyPortMemberIterator iter(a); int count = 0; while (iter.hasNext()) { LibertyPort *member = iter.next(); EXPECT_NE(member, nullptr); count++; } // Scalar port may have 0 members in the member iterator // (it iterates bus bits, not the port itself) EXPECT_GE(count, 0); } TEST_F(StaLibertyTest, LibraryNominalValues) { // The library should have nominal PVT values from parsing float process = lib_->nominalProcess(); float voltage = lib_->nominalVoltage(); float temperature = lib_->nominalTemperature(); // These should be non-zero for a real library EXPECT_GT(voltage, 0.0f); EXPECT_GE(process, 0.0f); EXPECT_GE(temperature, 0.0f); } TEST_F(StaLibertyTest, LibraryThresholds) { float in_rise = lib_->inputThreshold(RiseFall::rise()); float in_fall = lib_->inputThreshold(RiseFall::fall()); float out_rise = lib_->outputThreshold(RiseFall::rise()); float out_fall = lib_->outputThreshold(RiseFall::fall()); float slew_lower_rise = lib_->slewLowerThreshold(RiseFall::rise()); float slew_upper_rise = lib_->slewUpperThreshold(RiseFall::rise()); float slew_derate = lib_->slewDerateFromLibrary(); EXPECT_GT(in_rise, 0.0f); EXPECT_GT(in_fall, 0.0f); EXPECT_GT(out_rise, 0.0f); EXPECT_GT(out_fall, 0.0f); EXPECT_GT(slew_lower_rise, 0.0f); EXPECT_GT(slew_upper_rise, 0.0f); EXPECT_GT(slew_derate, 0.0f); } TEST_F(StaLibertyTest, LibraryDelayModelType) { DelayModelType model_type = lib_->delayModelType(); // Nangate45 should use table model EXPECT_EQ(model_type, DelayModelType::table); } TEST_F(StaLibertyTest, CellHasSequentials) { LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); if (dff) { EXPECT_TRUE(dff->hasSequentials()); auto &seqs = dff->sequentials(); EXPECT_GT(seqs.size(), 0u); } } TEST_F(StaLibertyTest, CellOutputPortSequential) { ASSERT_NO_THROW(( [&](){ LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); if (dff) { LibertyPort *q = dff->findLibertyPort("Q"); if (q) { Sequential *seq = dff->outputPortSequential(q); // outputPortSequential may return nullptr depending on cell definition if (seq) { EXPECT_EQ(seq->output(), q); } } } }() )); } TEST_F(StaLibertyTest, LibraryBuffersAndInverters) { LibertyCellSeq *bufs = lib_->buffers(); EXPECT_NE(bufs, nullptr); // Nangate45 should have buffer cells EXPECT_GT(bufs->size(), 0u); LibertyCellSeq *invs = lib_->inverters(); EXPECT_NE(invs, nullptr); EXPECT_GT(invs->size(), 0u); } TEST_F(StaLibertyTest, CellFindTimingArcSet) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); // Find by index TimingArcSet *found = buf->findTimingArcSet(static_cast(0)); EXPECT_NE(found, nullptr); } TEST_F(StaLibertyTest, CellLeakagePower) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); float leakage; bool exists; buf->leakagePower(leakage, exists); // Nangate45 may or may not have cell-level leakage power if (exists) { EXPECT_GE(leakage, 0.0f); } } TEST_F(StaLibertyTest, TimingArcSetFindTimingArc) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *arcset = arcsets[0]; auto &arcs = arcset->arcs(); ASSERT_GT(arcs.size(), 0u); TimingArc *found = arcset->findTimingArc(0); EXPECT_NE(found, nullptr); } TEST_F(StaLibertyTest, TimingArcSetWire) { // Test the static wire timing arc set TimingArcSet *wire_set = TimingArcSet::wireTimingArcSet(); EXPECT_NE(wire_set, nullptr); EXPECT_EQ(TimingArcSet::wireArcCount(), 2); int rise_idx = TimingArcSet::wireArcIndex(RiseFall::rise()); int fall_idx = TimingArcSet::wireArcIndex(RiseFall::fall()); EXPECT_NE(rise_idx, fall_idx); } TEST_F(StaLibertyTest, InternalPowerCompute) { LibertyCell *inv = lib_->findLibertyCell("INV_X1"); ASSERT_NE(inv, nullptr); auto &powers = inv->internalPowers(); if (powers.size() > 0) { const InternalPower &pwr = powers[0]; // Compute power with some slew and cap values float power_val = pwr.power(RiseFall::rise(), nullptr, 0.1f, 0.01f); // Power value can be negative depending on library data EXPECT_FALSE(std::isinf(power_val)); } } TEST_F(StaLibertyTest, PortDriverWaveform) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *z = buf->findLibertyPort("Z"); ASSERT_NE(z, nullptr); DriverWaveform *dw_rise = z->driverWaveform(RiseFall::rise()); DriverWaveform *dw_fall = z->driverWaveform(RiseFall::fall()); // BUF_X1 does not have driver waveform definitions EXPECT_EQ(dw_rise, nullptr); EXPECT_EQ(dw_fall, nullptr); } TEST_F(StaLibertyTest, PortVoltageName) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *a = buf->findLibertyPort("A"); ASSERT_NE(a, nullptr); const std::string &vname = a->voltageName(); // vname may be empty for simple cells (void)vname; } TEST_F(StaLibertyTest, PortEquivAndLess) { LibertyCell *and2 = lib_->findLibertyCell("AND2_X1"); if (and2) { LibertyPort *a1 = and2->findLibertyPort("A1"); LibertyPort *a2 = and2->findLibertyPort("A2"); LibertyPort *zn = and2->findLibertyPort("ZN"); if (a1 && a2 && zn) { // Same port should be equiv EXPECT_TRUE(LibertyPort::equiv(a1, a1)); // Different ports bool less12 = LibertyPort::less(a1, a2); bool less21 = LibertyPort::less(a2, a1); EXPECT_FALSE(less12 && less21); } } } TEST_F(StaLibertyTest, PortIntrinsicDelay) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *z = buf->findLibertyPort("Z"); ASSERT_NE(z, nullptr); ArcDelay delay = z->intrinsicDelay(sta_); EXPECT_GE(delayAsFloat(delay), 0.0f); ArcDelay delay_rf = z->intrinsicDelay(RiseFall::rise(), MinMax::max(), sta_); EXPECT_GE(delayAsFloat(delay_rf), 0.0f); } TEST_F(StaLibertyTest, CellLatchEnable) { ASSERT_NO_THROW(( [&](){ LibertyCell *dlatch = lib_->findLibertyCell("DLATCH_X1"); if (dlatch) { auto &arcsets = dlatch->timingArcSets(); for (auto *arcset : arcsets) { const LibertyPort *enable_port; const FuncExpr *enable_func; const RiseFall *enable_rf; dlatch->latchEnable(arcset, enable_port, enable_func, enable_rf); EXPECT_NE(enable_port, nullptr); EXPECT_NE(enable_func, nullptr); EXPECT_NE(enable_rf, nullptr); } } }() )); } TEST_F(StaLibertyTest, CellClockGateFlags) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isClockGate()); EXPECT_FALSE(buf->isClockGateLatchPosedge()); EXPECT_FALSE(buf->isClockGateLatchNegedge()); EXPECT_FALSE(buf->isClockGateOther()); } TEST_F(StaLibertyTest, GateTableModelDriveResistanceAndDelay) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); auto &arcs = arcsets[0]->arcs(); ASSERT_GT(arcs.size(), 0u); TimingArc *arc = arcs[0]; GateTableModel *gtm = arc->gateTableModel(); if (gtm) { // Test gate delay float delay_f, slew_f; gtm->gateDelay(nullptr, 0.1f, 0.01f, delay_f, slew_f); // Delay values can be negative depending on library data EXPECT_FALSE(std::isinf(delay_f)); EXPECT_GE(slew_f, 0.0f); // Test drive resistance float res = gtm->driveResistance(nullptr); EXPECT_GE(res, 0.0f); // Test report std::string report = gtm->reportGateDelay(nullptr, 0.1f, 0.01f, MinMax::max(), PocvMode::scalar, 3); EXPECT_FALSE(report.empty()); // Test model accessors const TableModel *delay_model = gtm->delayModel(); EXPECT_NE(delay_model, nullptr); const TableModel *slew_model = gtm->slewModel(); EXPECT_NE(slew_model, nullptr); // receiverModel and outputWaveforms are null for this library const ReceiverModel *rm = gtm->receiverModel(); EXPECT_EQ(rm, nullptr); OutputWaveforms *ow = gtm->outputWaveforms(); EXPECT_EQ(ow, nullptr); } } TEST_F(StaLibertyTest, LibraryScaleFactors) { ScaleFactors *sf = lib_->scaleFactors(); // May or may not have scale factors EXPECT_NE(sf, nullptr); float sf_val = lib_->scaleFactor(ScaleFactorType::cell, nullptr); EXPECT_FLOAT_EQ(sf_val, 1.0f); } TEST_F(StaLibertyTest, LibraryDefaultPinCaps) { ASSERT_NO_THROW(( [&](){ float input_cap = lib_->defaultInputPinCap(); float output_cap = lib_->defaultOutputPinCap(); float bidirect_cap = lib_->defaultBidirectPinCap(); EXPECT_GE(input_cap, 0.0f); EXPECT_GE(output_cap, 0.0f); EXPECT_GE(bidirect_cap, 0.0f); }() )); } TEST_F(StaLibertyTest, LibraryUnits) { const Units *units = lib_->units(); EXPECT_NE(units, nullptr); Units *units_nc = lib_->units(); EXPECT_NE(units_nc, nullptr); } TEST_F(StaLibertyTest, CellScaleFactors) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); ScaleFactors *sf = buf->scaleFactors(); // sf may be null depending on cell type } TEST_F(StaLibertyTest, CellOcvArcDepth) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); float depth = buf->ocvArcDepth(); EXPECT_GE(depth, 0.0f); } TEST_F(StaLibertyTest, CellOcvDerate) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); OcvDerate *derate = buf->ocvDerate(); // derate may be null depending on cell type } TEST_F(StaLibertyTest, LibraryOcvDerate) { OcvDerate *derate = lib_->defaultOcvDerate(); // NangateOpenCellLibrary does not define OCV derate EXPECT_EQ(derate, nullptr); float depth = lib_->ocvArcDepth(); EXPECT_GE(depth, 0.0f); } //////////////////////////////////////////////////////////////// // Helper to create FloatSeq from initializer list //////////////////////////////////////////////////////////////// static FloatSeq makeFloatSeqVal(std::initializer_list vals) { FloatSeq seq; for (float v : vals) seq.push_back(v); return seq; } static FloatSeq *makeFloatSeq(std::initializer_list vals) { FloatSeq *seq = new FloatSeq; for (float v : vals) seq->push_back(v); return seq; } static TableAxisPtr makeTestAxis(TableAxisVariable var, std::initializer_list vals) { FloatSeq values = makeFloatSeqVal(vals); return std::make_shared(var, std::move(values)); } //////////////////////////////////////////////////////////////// // Table virtual method coverage (Table0/1/2/3 order, axis1, axis2) //////////////////////////////////////////////////////////////// TEST(TableVirtualTest, Table0Order) { Table t(1.5f); EXPECT_EQ(t.order(), 0); // Table base class axis1/axis2 return nullptr EXPECT_EQ(t.axis1(), nullptr); EXPECT_EQ(t.axis2(), nullptr); } TEST(TableVirtualTest, Table1OrderAndAxis) { FloatSeq *vals = makeFloatSeq({1.0f, 2.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f}); Table t(vals, axis); EXPECT_EQ(t.order(), 1); EXPECT_NE(t.axis1(), nullptr); EXPECT_EQ(t.axis2(), nullptr); } TEST(TableVirtualTest, Table2OrderAndAxes) { FloatTable vals; vals.push_back({1.0f, 2.0f}); vals.push_back({3.0f, 4.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {0.1f, 0.2f}); Table t(std::move(vals), ax1, ax2); EXPECT_EQ(t.order(), 2); EXPECT_NE(t.axis1(), nullptr); EXPECT_NE(t.axis2(), nullptr); EXPECT_EQ(t.axis3(), nullptr); } TEST(TableVirtualTest, Table3OrderAndAxes) { FloatTable vals; vals.push_back({1.0f, 2.0f}); vals.push_back({3.0f, 4.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {0.1f, 0.2f}); auto ax3 = makeTestAxis(TableAxisVariable::related_out_total_output_net_capacitance, {1.0f}); Table t(std::move(vals), ax1, ax2, ax3); EXPECT_EQ(t.order(), 3); EXPECT_NE(t.axis1(), nullptr); EXPECT_NE(t.axis2(), nullptr); EXPECT_NE(t.axis3(), nullptr); } //////////////////////////////////////////////////////////////// // Table report() / reportValue() methods //////////////////////////////////////////////////////////////// TEST(TableReportTest, Table0ReportValue) { Table t(42.0f); Unit unit(1e-9f, "s", 3); std::string rv = t.reportValue("delay", nullptr, nullptr, 0.0f, "", 0.0f, 0.0f, &unit, 3); EXPECT_FALSE(rv.empty()); } // Table1/2/3::reportValue dereferences cell->libertyLibrary()->units() // so they need a real cell. Tested via StaLibertyTest fixture below. //////////////////////////////////////////////////////////////// // Table destruction coverage //////////////////////////////////////////////////////////////// TEST(TableDestructTest, Table1Destruct) { ASSERT_NO_THROW(( [&](){ FloatSeq *vals = makeFloatSeq({1.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f}); Table *t = new Table(vals, axis); delete t; // covers Table::~Table (order 1) }() )); } TEST(TableDestructTest, Table2Destruct) { ASSERT_NO_THROW(( [&](){ FloatTable vals; vals.push_back({1.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {0.1f}); Table *t = new Table(std::move(vals), ax1, ax2); delete t; // covers Table::~Table (order 2) }() )); } TEST(TableDestructTest, Table3Destruct) { ASSERT_NO_THROW(( [&](){ FloatTable vals; vals.push_back({1.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {0.1f}); auto ax3 = makeTestAxis(TableAxisVariable::related_out_total_output_net_capacitance, {1.0f}); Table *t = new Table(std::move(vals), ax1, ax2, ax3); delete t; // covers Table::~Table (order 3) }() )); } //////////////////////////////////////////////////////////////// // TableModel::value coverage //////////////////////////////////////////////////////////////// TEST(TableModelValueTest, ValueByIndex) { Table *tbl = new Table(5.5f); TablePtr table_ptr(tbl); TableTemplate *tmpl = new TableTemplate("test_tmpl"); TableModel model(table_ptr, tmpl, ScaleFactorType::cell, RiseFall::rise()); float v = model.value(0, 0, 0); EXPECT_FLOAT_EQ(v, 5.5f); delete tmpl; } //////////////////////////////////////////////////////////////// // Pvt destructor coverage //////////////////////////////////////////////////////////////// TEST(PvtDestructTest, CreateAndDestroy) { // Pvt(process, voltage, temperature) Pvt *pvt = new Pvt(1.1f, 1.0f, 25.0f); EXPECT_FLOAT_EQ(pvt->process(), 1.1f); EXPECT_FLOAT_EQ(pvt->voltage(), 1.0f); EXPECT_FLOAT_EQ(pvt->temperature(), 25.0f); delete pvt; // covers Pvt::~Pvt } //////////////////////////////////////////////////////////////// // ScaleFactors::report coverage //////////////////////////////////////////////////////////////// TEST(ScaleFactorsPrintTest, Print) { ASSERT_NO_THROW(( [&](){ Report *report = makeReportStd(); ScaleFactors sf("test_sf"); sf.setScale(ScaleFactorType::cell, ScaleFactorPvt::process, RiseFall::rise(), 1.0f); sf.report(report); // covers ScaleFactors::report() delete report; }() )); } //////////////////////////////////////////////////////////////// // GateTableModel / CheckTableModel static checkAxes //////////////////////////////////////////////////////////////// TEST(GateTableModelCheckAxesTest, ValidAxes) { FloatTable vals; vals.push_back({1.0f, 2.0f}); vals.push_back({3.0f, 4.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {0.1f, 0.2f}); TablePtr tbl = std::make_shared(std::move(vals), ax1, ax2); TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise()); EXPECT_TRUE(GateTableModel::checkAxes(&tbl_model)); } TEST(GateTableModelCheckAxesTest, InvalidAxis) { FloatSeq *vals = makeFloatSeq({1.0f, 2.0f}); auto axis = makeTestAxis(TableAxisVariable::constrained_pin_transition, {0.01f, 0.02f}); TablePtr tbl = std::make_shared
(vals, axis); TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise()); EXPECT_FALSE(GateTableModel::checkAxes(&tbl_model)); } TEST(GateTableModelCheckAxesTest, Table0NoAxes) { TablePtr tbl = std::make_shared
(1.0f); TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise()); EXPECT_TRUE(GateTableModel::checkAxes(&tbl_model)); } TEST(CheckTableModelCheckAxesTest, ValidAxes) { FloatTable vals; vals.push_back({1.0f, 2.0f}); vals.push_back({3.0f, 4.0f}); auto ax1 = makeTestAxis(TableAxisVariable::related_pin_transition, {0.01f, 0.02f}); auto ax2 = makeTestAxis(TableAxisVariable::constrained_pin_transition, {0.1f, 0.2f}); TablePtr tbl = std::make_shared
(std::move(vals), ax1, ax2); TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise()); EXPECT_TRUE(CheckTableModel::checkAxes(&tbl_model)); } TEST(CheckTableModelCheckAxesTest, InvalidAxis) { FloatSeq *vals = makeFloatSeq({1.0f, 2.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f}); TablePtr tbl = std::make_shared
(vals, axis); TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise()); EXPECT_FALSE(CheckTableModel::checkAxes(&tbl_model)); } TEST(CheckTableModelCheckAxesTest, Table0NoAxes) { TablePtr tbl = std::make_shared
(1.0f); TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise()); EXPECT_TRUE(CheckTableModel::checkAxes(&tbl_model)); } TEST(ReceiverModelCheckAxesTest, ValidAxes) { FloatSeq *vals = makeFloatSeq({1.0f, 2.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f}); TablePtr tbl = std::make_shared
(vals, axis); TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise()); EXPECT_TRUE(ReceiverModel::checkAxes(&tbl_model)); } TEST(ReceiverModelCheckAxesTest, Table0NoAxis) { TablePtr tbl = std::make_shared
(1.0f); TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise()); EXPECT_FALSE(ReceiverModel::checkAxes(&tbl_model)); } //////////////////////////////////////////////////////////////// // DriverWaveform //////////////////////////////////////////////////////////////// TEST(DriverWaveformTest, CreateAndName) { // DriverWaveform::waveform() expects a Table with axis1=slew, axis2=voltage (order 2) FloatTable vals; vals.push_back({0.0f, 1.0f}); vals.push_back({0.5f, 1.5f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.1f, 0.2f}); auto ax2 = makeTestAxis(TableAxisVariable::normalized_voltage, {0.0f, 1.0f}); TablePtr tbl = std::make_shared
(std::move(vals), ax1, ax2); DriverWaveform *dw = new DriverWaveform("test_driver_waveform", tbl); EXPECT_EQ(dw->name(), "test_driver_waveform"); Table wf = dw->waveform(0.15f); // Waveform accessor exercised; axis may be null for simple waveforms EXPECT_EQ(wf.order(), 1); delete dw; } // InternalPowerAttrs has been removed in MCMM update //////////////////////////////////////////////////////////////// // LibertyCellPortBitIterator destructor coverage //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellPortBitIteratorDestruction) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyCellPortBitIterator *iter = new LibertyCellPortBitIterator(buf); int count = 0; while (iter->hasNext()) { LibertyPort *p = iter->next(); EXPECT_NE(p, nullptr); count++; } EXPECT_GT(count, 0); delete iter; // covers ~LibertyCellPortBitIterator } //////////////////////////////////////////////////////////////// // LibertyPort setter coverage (using parsed ports) //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, PortSetIsPad) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); bool orig = port->isPad(); port->setIsPad(true); EXPECT_TRUE(port->isPad()); port->setIsPad(orig); } TEST_F(StaLibertyTest, PortSetIsSwitch) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); port->setIsSwitch(true); EXPECT_TRUE(port->isSwitch()); port->setIsSwitch(false); } TEST_F(StaLibertyTest, PortSetIsPllFeedback) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); port->setIsPllFeedback(true); EXPECT_TRUE(port->isPllFeedback()); port->setIsPllFeedback(false); } TEST_F(StaLibertyTest, PortSetIsCheckClk) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); port->setIsCheckClk(true); EXPECT_TRUE(port->isCheckClk()); port->setIsCheckClk(false); } TEST_F(StaLibertyTest, PortSetPulseClk) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); port->setPulseClk(RiseFall::rise(), RiseFall::fall()); EXPECT_EQ(port->pulseClkTrigger(), RiseFall::rise()); EXPECT_EQ(port->pulseClkSense(), RiseFall::fall()); port->setPulseClk(nullptr, nullptr); } TEST_F(StaLibertyTest, PortSetFanoutLoad) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); port->setFanoutLoad(2.5f); float fanout; bool exists; port->fanoutLoad(fanout, exists); EXPECT_TRUE(exists); EXPECT_FLOAT_EQ(fanout, 2.5f); } TEST_F(StaLibertyTest, PortSetFanoutLimit) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("Z"); ASSERT_NE(port, nullptr); port->setFanoutLimit(10.0f, MinMax::max()); float limit; bool exists; port->fanoutLimit(MinMax::max(), limit, exists); EXPECT_TRUE(exists); EXPECT_FLOAT_EQ(limit, 10.0f); } TEST_F(StaLibertyTest, PortBundlePort) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); LibertyPort *bundle = port->bundlePort(); EXPECT_EQ(bundle, nullptr); } TEST_F(StaLibertyTest, PortFindLibertyBusBit) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); LibertyPort *bit = port->findLibertyBusBit(0); EXPECT_EQ(bit, nullptr); } // findLibertyMember(0) on scalar port crashes (member_ports_ is nullptr) // Would need a bus port to test this safely. TEST_F(StaLibertyTest, PortCornerPort) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); Scene *scene = sta_->cmdScene(); ASSERT_NE(scene, nullptr); LibertyPort *cp = port->scenePort(scene, MinMax::min()); EXPECT_NE(cp, nullptr); const LibertyPort *ccp = static_cast(port)->scenePort(scene, MinMax::min()); EXPECT_NE(ccp, nullptr); } TEST_F(StaLibertyTest, PortClkTreeDelay) { LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); ASSERT_NE(dff, nullptr); LibertyPort *clk = dff->findLibertyPort("CK"); ASSERT_NE(clk, nullptr); float d = clk->clkTreeDelay(0.1f, RiseFall::rise(), RiseFall::rise(), MinMax::max()); EXPECT_GE(d, 0.0f); } // setMemberFloat is protected - skip //////////////////////////////////////////////////////////////// // ModeValueDef::setSdfCond and setCond coverage //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, ModeValueDefSetSdfCond) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); ModeDef *mode_def = buf->makeModeDef("test_mode"); ASSERT_NE(mode_def, nullptr); ModeValueDef *val_def = mode_def->defineValue("val1"); ASSERT_NE(val_def, nullptr); EXPECT_EQ(val_def->value(), "val1"); // Set sdf_cond after creation val_def->setSdfCond("orig_sdf_cond"); EXPECT_EQ(val_def->sdfCond(), "orig_sdf_cond"); val_def->setSdfCond("new_sdf_cond"); EXPECT_EQ(val_def->sdfCond(), "new_sdf_cond"); } TEST_F(StaLibertyTest, ModeValueDefSetCond) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); ModeDef *mode_def = buf->makeModeDef("test_mode2"); ASSERT_NE(mode_def, nullptr); ModeValueDef *val_def = mode_def->defineValue("val2"); ASSERT_NE(val_def, nullptr); EXPECT_EQ(val_def->cond(), nullptr); val_def->setCond(nullptr); EXPECT_EQ(val_def->cond(), nullptr); } //////////////////////////////////////////////////////////////// // LibertyCell::latchCheckEnableEdge //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellLatchCheckEnableEdgeWithDFF) { LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); ASSERT_NE(dff, nullptr); auto &arcsets = dff->timingArcSets(); if (!arcsets.empty()) { const RiseFall *edge = dff->latchCheckEnableEdge(arcsets[0]); // DFF_X1 is a flip-flop, not a latch; latchCheckEnableEdge returns nullptr if (edge) { EXPECT_TRUE(edge == RiseFall::rise() || edge == RiseFall::fall()); } } } //////////////////////////////////////////////////////////////// // LibertyCell::sceneCell //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellCornerCell) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyCell *cc = buf->sceneCell(0); EXPECT_NE(cc, nullptr); } //////////////////////////////////////////////////////////////// // TimingArcSet::less (static) //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, TimingArcSetLessStatic) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GE(arcsets.size(), 1u); bool result = TimingArcSet::less(arcsets[0], arcsets[0]); EXPECT_FALSE(result); if (arcsets.size() >= 2) { bool r1 = TimingArcSet::less(arcsets[0], arcsets[1]); bool r2 = TimingArcSet::less(arcsets[1], arcsets[0]); EXPECT_FALSE(r1 && r2); } } //////////////////////////////////////////////////////////////// // TimingArc::sceneArc //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, TimingArcCornerArc) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); auto &arcs = arcsets[0]->arcs(); ASSERT_GT(arcs.size(), 0u); const TimingArc *corner = arcs[0]->sceneArc(0); EXPECT_NE(corner, nullptr); } //////////////////////////////////////////////////////////////// // TimingArcSet setters //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, TimingArcSetSetRole) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *set = arcsets[0]; const TimingRole *orig = set->role(); set->setRole(TimingRole::setup()); EXPECT_EQ(set->role(), TimingRole::setup()); set->setRole(orig); } TEST_F(StaLibertyTest, TimingArcSetSetIsCondDefaultExplicit) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *set = arcsets[0]; bool orig = set->isCondDefault(); set->setIsCondDefault(true); EXPECT_TRUE(set->isCondDefault()); set->setIsCondDefault(orig); } // isDisabledConstraint/setIsDisabledConstraint removed from TimingArcSet //////////////////////////////////////////////////////////////// // GateTableModel::gateDelay deprecated 7-arg version //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, GateTableModelGateDelayDeprecated) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); auto &arcs = arcsets[0]->arcs(); ASSERT_GT(arcs.size(), 0u); GateTableModel *gtm = arcs[0]->gateTableModel(); if (gtm) { float delay_f, slew_f; gtm->gateDelay(nullptr, 0.1f, 0.01f, delay_f, slew_f); // Delay values can be negative depending on library data EXPECT_FALSE(std::isinf(delay_f)); EXPECT_GE(slew_f, 0.0f); } } //////////////////////////////////////////////////////////////// // CheckTableModel via Sta (setup/hold arcs) //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CheckTableModelCheckDelay) { LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); ASSERT_NE(dff, nullptr); auto &arcsets = dff->timingArcSets(); for (auto *set : arcsets) { const TimingRole *role = set->role(); if (role == TimingRole::setup() || role == TimingRole::hold()) { auto &arcs = set->arcs(); if (!arcs.empty()) { TimingModel *model = arcs[0]->model(); CheckTableModel *ctm = dynamic_cast(model); if (ctm) { ArcDelay d = ctm->checkDelay(nullptr, 0.1f, 0.1f, 0.0f, MinMax::max(), PocvMode::scalar); EXPECT_GE(delayAsFloat(d), 0.0f); std::string rpt = ctm->reportCheckDelay(nullptr, 0.1f, "", 0.1f, 0.0f, MinMax::max(), PocvMode::scalar, 3); EXPECT_FALSE(rpt.empty()); return; } } } } } //////////////////////////////////////////////////////////////// // Library addDriverWaveform / findDriverWaveform //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, LibraryMakeAndFindDriverWaveform) { FloatSeq *vals = makeFloatSeq({0.0f, 1.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {0.0f, 1.0f}); TablePtr tbl = std::make_shared
(vals, axis); DriverWaveform *dw = lib_->makeDriverWaveform("my_driver_wf", tbl); ASSERT_NE(dw, nullptr); DriverWaveform *found = lib_->findDriverWaveform("my_driver_wf"); EXPECT_EQ(found, dw); EXPECT_EQ(found->name(), "my_driver_wf"); EXPECT_EQ(lib_->findDriverWaveform("no_such_wf"), nullptr); } //////////////////////////////////////////////////////////////// // TableModel::report (via StaLibertyTest) //////////////////////////////////////////////////////////////// // TableModel::reportValue needs non-null table_unit and may dereference null pvt // Covered via GateTableModel::reportGateDelay which exercises the same code path. //////////////////////////////////////////////////////////////// // Port setDriverWaveform //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, PortSetDriverWaveform) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("Z"); ASSERT_NE(port, nullptr); FloatSeq *vals = makeFloatSeq({0.0f, 1.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {0.0f, 1.0f}); TablePtr tbl = std::make_shared
(vals, axis); DriverWaveform *dw = lib_->makeDriverWaveform("port_dw", tbl); port->setDriverWaveform(dw, RiseFall::rise()); DriverWaveform *got = port->driverWaveform(RiseFall::rise()); EXPECT_EQ(got, dw); } //////////////////////////////////////////////////////////////// // LibertyCell::setTestCell / findModeDef //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellSetTestCell) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); TestCell *tc = buf->testCell(); // BUF_X1 does not have a test cell EXPECT_EQ(tc, nullptr); buf->setTestCell(nullptr); EXPECT_EQ(buf->testCell(), nullptr); } TEST_F(StaLibertyTest, CellFindModeDef) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); const ModeDef *md = buf->findModeDef("nonexistent_mode"); EXPECT_EQ(md, nullptr); ModeDef *created = buf->makeModeDef("my_mode"); ASSERT_NE(created, nullptr); const ModeDef *found = buf->findModeDef("my_mode"); EXPECT_EQ(found, created); } //////////////////////////////////////////////////////////////// // Library wireload defaults //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, LibraryWireloadDefaults) { ASSERT_NO_THROW(( [&](){ const Wireload *wl = lib_->defaultWireload(); EXPECT_NE(wl, nullptr); WireloadMode mode = lib_->defaultWireloadMode(); EXPECT_GE(static_cast(mode), 0); }() )); } //////////////////////////////////////////////////////////////// // GateTableModel with Table0 //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, GateTableModelWithTable0Delay) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); Table *delay_tbl = new Table(1.0e-10f); TablePtr delay_ptr(delay_tbl); Table *slew_tbl = new Table(2.0e-10f); TablePtr slew_ptr(slew_tbl); TableTemplate *tmpl = new TableTemplate("test_tmpl2"); TableModel *delay_model = new TableModel(delay_ptr, tmpl, ScaleFactorType::cell, RiseFall::rise()); TableModel *slew_model = new TableModel(slew_ptr, tmpl, ScaleFactorType::cell, RiseFall::rise()); // Wrap TableModel in TableModels as required by GateTableModel constructor TableModels *delay_models = new TableModels(delay_model); TableModels *slew_models = new TableModels(slew_model); GateTableModel *gtm = new GateTableModel(buf, delay_models, slew_models); float d, s; gtm->gateDelay(nullptr, 0.0f, 0.0f, d, s); EXPECT_GE(d, 0.0f); EXPECT_GE(s, 0.0f); float res = gtm->driveResistance(nullptr); EXPECT_GE(res, 0.0f); std::string rpt = gtm->reportGateDelay(nullptr, 0.0f, 0.0f, MinMax::max(), PocvMode::scalar, 3); EXPECT_FALSE(rpt.empty()); delete gtm; delete tmpl; } //////////////////////////////////////////////////////////////// // CheckTableModel direct creation //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CheckTableModelDirect) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); Table *check_tbl = new Table(5.0e-11f); TablePtr check_ptr(check_tbl); TableTemplate *tmpl = new TableTemplate("check_tmpl"); TableModel *model = new TableModel(check_ptr, tmpl, ScaleFactorType::cell, RiseFall::rise()); // Wrap TableModel in TableModels as required by CheckTableModel constructor TableModels *check_models = new TableModels(model); CheckTableModel *ctm = new CheckTableModel(buf, check_models); ArcDelay d = ctm->checkDelay(nullptr, 0.1f, 0.1f, 0.0f, MinMax::max(), PocvMode::scalar); EXPECT_GE(delayAsFloat(d), 0.0f); std::string rpt = ctm->reportCheckDelay(nullptr, 0.1f, "", 0.1f, 0.0f, MinMax::max(), PocvMode::scalar, 3); EXPECT_FALSE(rpt.empty()); const TableModel *m = ctm->checkModel(); EXPECT_NE(m, nullptr); delete ctm; delete tmpl; } //////////////////////////////////////////////////////////////// // Table findValue / value coverage //////////////////////////////////////////////////////////////// TEST(TableLookupTest, Table0FindValue) { Table t(7.5f); float v = t.findValue(0.0f, 0.0f, 0.0f); EXPECT_FLOAT_EQ(v, 7.5f); float v2 = t.value(0, 0, 0); EXPECT_FLOAT_EQ(v2, 7.5f); } TEST(TableLookupTest, Table1FindValue) { FloatSeq *vals = makeFloatSeq({10.0f, 20.0f, 30.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {1.0f, 2.0f, 3.0f}); Table t(vals, axis); float v = t.findValue(1.0f, 0.0f, 0.0f); EXPECT_FLOAT_EQ(v, 10.0f); float v2 = t.findValue(1.5f, 0.0f, 0.0f); EXPECT_NEAR(v2, 15.0f, 0.1f); } TEST(TableLookupTest, Table2FindValue) { FloatTable vals; vals.push_back({1.0f, 2.0f}); vals.push_back({3.0f, 4.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {1.0f, 2.0f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {10.0f, 20.0f}); Table t(std::move(vals), ax1, ax2); float v = t.findValue(1.0f, 10.0f, 0.0f); EXPECT_FLOAT_EQ(v, 1.0f); } TEST(TableLookupTest, Table3Value) { FloatTable vals; vals.push_back({1.0f, 2.0f}); vals.push_back({3.0f, 4.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {0.1f, 0.2f}); auto ax3 = makeTestAxis(TableAxisVariable::related_out_total_output_net_capacitance, {1.0f}); Table t(std::move(vals), ax1, ax2, ax3); float v = t.value(0, 0, 0); EXPECT_FLOAT_EQ(v, 1.0f); } //////////////////////////////////////////////////////////////// // LibertyCell::findTimingArcSet by pointer //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellFindTimingArcSetByPtr) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); TimingArcSet *found = buf->findTimingArcSet(arcsets[0]); EXPECT_EQ(found, arcsets[0]); } //////////////////////////////////////////////////////////////// // LibertyCell::addScaledCell //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellAddScaledCell) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); OperatingConditions *oc = new OperatingConditions("test_oc"); TestCell *tc = new TestCell(lib_, "scaled_buf", "test.lib"); buf->addScaledCell(oc, tc); } //////////////////////////////////////////////////////////////// // LibertyCell property tests //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellInverterCheck) { LibertyCell *inv = lib_->findLibertyCell("INV_X1"); ASSERT_NE(inv, nullptr); EXPECT_TRUE(inv->isInverter()); } TEST_F(StaLibertyTest, CellFootprint) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); const std::string &fp = buf->footprint(); // fp may be empty for simple cells (void)fp; buf->setFootprint("test_fp"); EXPECT_EQ(buf->footprint(), "test_fp"); } TEST_F(StaLibertyTest, CellUserFunctionClass) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); const std::string &ufc = buf->userFunctionClass(); // ufc may be empty for simple cells (void)ufc; buf->setUserFunctionClass("my_class"); EXPECT_EQ(buf->userFunctionClass(), "my_class"); } TEST_F(StaLibertyTest, CellSetArea) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); float orig = buf->area(); buf->setArea(99.9f); EXPECT_FLOAT_EQ(buf->area(), 99.9f); buf->setArea(orig); } TEST_F(StaLibertyTest, CellSetOcvArcDepth) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); buf->setOcvArcDepth(0.5f); EXPECT_FLOAT_EQ(buf->ocvArcDepth(), 0.5f); } // isDisabledConstraint/setIsDisabledConstraint removed from LibertyCell TEST_F(StaLibertyTest, CellSetScaleFactors) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); ScaleFactors *sf = new ScaleFactors("my_sf"); buf->setScaleFactors(sf); EXPECT_EQ(buf->scaleFactors(), sf); } TEST_F(StaLibertyTest, CellSetHasInferedRegTimingArcs) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); buf->setHasInferedRegTimingArcs(true); buf->setHasInferedRegTimingArcs(false); } TEST_F(StaLibertyTest, CellAddBusDcl) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); BusDcl *bd = buf->makeBusDcl("test_bus", 0, 3); EXPECT_NE(bd, nullptr); } //////////////////////////////////////////////////////////////// // TableTemplate coverage //////////////////////////////////////////////////////////////// TEST(TableTemplateExtraTest, SetAxes) { TableTemplate tmpl("my_template"); EXPECT_EQ(tmpl.name(), "my_template"); EXPECT_EQ(tmpl.axis1(), nullptr); EXPECT_EQ(tmpl.axis2(), nullptr); EXPECT_EQ(tmpl.axis3(), nullptr); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {1.0f, 2.0f}); tmpl.setAxis1(ax1); EXPECT_NE(tmpl.axis1(), nullptr); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {0.1f, 0.2f}); tmpl.setAxis2(ax2); EXPECT_NE(tmpl.axis2(), nullptr); auto ax3 = makeTestAxis(TableAxisVariable::related_out_total_output_net_capacitance, {1.0f}); tmpl.setAxis3(ax3); EXPECT_NE(tmpl.axis3(), nullptr); tmpl.setName("renamed"); EXPECT_EQ(tmpl.name(), "renamed"); } //////////////////////////////////////////////////////////////// // OcvDerate coverage //////////////////////////////////////////////////////////////// TEST(OcvDerateTest, CreateAndAccess) { OcvDerate *derate = new OcvDerate("test_derate"); EXPECT_EQ(derate->name(), "test_derate"); const Table *tbl = derate->derateTable(RiseFall::rise(), EarlyLate::early(), PathType::clk); EXPECT_EQ(tbl, nullptr); tbl = derate->derateTable(RiseFall::fall(), EarlyLate::late(), PathType::data); EXPECT_EQ(tbl, nullptr); delete derate; } //////////////////////////////////////////////////////////////// // BusDcl coverage //////////////////////////////////////////////////////////////// TEST(BusDclTest, Create) { BusDcl bd("test_bus", 0, 7); EXPECT_EQ(bd.name(), "test_bus"); EXPECT_EQ(bd.from(), 0); EXPECT_EQ(bd.to(), 7); } //////////////////////////////////////////////////////////////// // OperatingConditions coverage //////////////////////////////////////////////////////////////// TEST(OperatingConditionsTest, Create) { OperatingConditions oc("typical"); EXPECT_EQ(oc.name(), "typical"); oc.setProcess(1.0f); oc.setTemperature(25.0f); oc.setVoltage(1.1f); EXPECT_FLOAT_EQ(oc.process(), 1.0f); EXPECT_FLOAT_EQ(oc.temperature(), 25.0f); EXPECT_FLOAT_EQ(oc.voltage(), 1.1f); } //////////////////////////////////////////////////////////////// // Table1 specific functions //////////////////////////////////////////////////////////////// TEST(Table1SpecificTest, FindValueClip) { FloatSeq *vals = makeFloatSeq({10.0f, 20.0f, 30.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {1.0f, 2.0f, 3.0f}); Table t(vals, axis); // Below range -> returns 0.0 float clipped_lo = t.findValueClip(0.5f); EXPECT_FLOAT_EQ(clipped_lo, 0.0f); // Above range -> returns last value float clipped_hi = t.findValueClip(4.0f); EXPECT_FLOAT_EQ(clipped_hi, 30.0f); // In range -> interpolated float clipped_mid = t.findValueClip(1.5f); EXPECT_NEAR(clipped_mid, 15.0f, 0.1f); } TEST(Table1SpecificTest, SingleArgFindValue) { FloatSeq *vals = makeFloatSeq({5.0f, 15.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {1.0f, 3.0f}); Table t(vals, axis); float v = t.findValue(2.0f); EXPECT_NEAR(v, 10.0f, 0.1f); } TEST(Table1SpecificTest, ValueByIndex) { FloatSeq *vals = makeFloatSeq({100.0f, 200.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {1.0f, 2.0f}); Table t(vals, axis); EXPECT_FLOAT_EQ(t.value(0), 100.0f); EXPECT_FLOAT_EQ(t.value(1), 200.0f); } //////////////////////////////////////////////////////////////// // Table2 specific functions //////////////////////////////////////////////////////////////// TEST(Table2SpecificTest, ValueByTwoIndices) { FloatTable vals; vals.push_back({1.0f, 2.0f}); vals.push_back({3.0f, 4.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {1.0f, 2.0f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {10.0f, 20.0f}); Table t(std::move(vals), ax1, ax2); EXPECT_FLOAT_EQ(t.value(0, 0), 1.0f); EXPECT_FLOAT_EQ(t.value(0, 1), 2.0f); EXPECT_FLOAT_EQ(t.value(1, 0), 3.0f); EXPECT_FLOAT_EQ(t.value(1, 1), 4.0f); FloatTable *vals3 = t.values3(); EXPECT_NE(vals3, nullptr); } //////////////////////////////////////////////////////////////// // Table1 move / copy constructors //////////////////////////////////////////////////////////////// TEST(Table1MoveTest, MoveConstruct) { FloatSeq *vals = makeFloatSeq({1.0f, 2.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f}); Table t1(vals, axis); Table t2(std::move(t1)); EXPECT_EQ(t2.order(), 1); EXPECT_NE(t2.axis1(), nullptr); } TEST(Table1MoveTest, CopyConstruct) { FloatSeq *vals = makeFloatSeq({1.0f, 2.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f}); Table t1(vals, axis); Table t2(t1); EXPECT_EQ(t2.order(), 1); EXPECT_NE(t2.axis1(), nullptr); } TEST(Table1MoveTest, MoveAssign) { FloatSeq *vals1 = makeFloatSeq({1.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f}); Table t1(vals1, ax1); FloatSeq *vals2 = makeFloatSeq({2.0f, 3.0f}); auto ax2 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f}); Table t2(vals2, ax2); t2 = std::move(t1); EXPECT_EQ(t2.order(), 1); } //////////////////////////////////////////////////////////////// // TableModel setScaleFactorType / setIsScaled //////////////////////////////////////////////////////////////// TEST(TableModelSetterTest, SetScaleFactorType) { ASSERT_NO_THROW(( [&](){ Table *tbl = new Table(1.0f); TablePtr tp(tbl); TableTemplate *tmpl = new TableTemplate("tmpl"); TableModel model(tp, tmpl, ScaleFactorType::cell, RiseFall::rise()); model.setScaleFactorType(ScaleFactorType::pin_cap); delete tmpl; }() )); } TEST(TableModelSetterTest, SetIsScaled) { ASSERT_NO_THROW(( [&](){ Table *tbl = new Table(1.0f); TablePtr tp(tbl); TableTemplate *tmpl = new TableTemplate("tmpl2"); TableModel model(tp, tmpl, ScaleFactorType::cell, RiseFall::rise()); model.setIsScaled(true); model.setIsScaled(false); delete tmpl; }() )); } //////////////////////////////////////////////////////////////// // Table base class setScaleFactorType / setIsScaled //////////////////////////////////////////////////////////////// // Table::setScaleFactorType and Table::setIsScaled are declared but not defined // in the library - skip these tests. //////////////////////////////////////////////////////////////// // TimingArcSet wire statics //////////////////////////////////////////////////////////////// TEST(TimingArcSetWireTest, WireTimingArcSet) { TimingArcSet *wire = TimingArcSet::wireTimingArcSet(); // wireTimingArcSet is null without Sta initialization EXPECT_EQ(wire, nullptr); int ri = TimingArcSet::wireArcIndex(RiseFall::rise()); int fi = TimingArcSet::wireArcIndex(RiseFall::fall()); EXPECT_NE(ri, fi); EXPECT_EQ(TimingArcSet::wireArcCount(), 2); } //////////////////////////////////////////////////////////////// // LibertyPort additional setters //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, PortSetRelatedGroundPort) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port_a = buf->findLibertyPort("A"); LibertyPort *port_z = buf->findLibertyPort("Z"); ASSERT_NE(port_a, nullptr); ASSERT_NE(port_z, nullptr); // Set and verify related ground port pointer port_a->setRelatedGroundPort(port_z); EXPECT_EQ(port_a->relatedGroundPort(), port_z); } TEST_F(StaLibertyTest, PortSetRelatedPowerPort) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port_a = buf->findLibertyPort("A"); LibertyPort *port_z = buf->findLibertyPort("Z"); ASSERT_NE(port_a, nullptr); ASSERT_NE(port_z, nullptr); // Set and verify related power port pointer port_a->setRelatedPowerPort(port_z); EXPECT_EQ(port_a->relatedPowerPort(), port_z); } // isDisabledConstraint has been moved from LibertyPort to Sdc. // This test is no longer applicable. TEST_F(StaLibertyTest, PortRegClkAndOutput) { LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); ASSERT_NE(dff, nullptr); LibertyPort *clk = dff->findLibertyPort("CK"); ASSERT_NE(clk, nullptr); bool is_reg_clk = clk->isRegClk(); // is_reg_clk value depends on cell type LibertyPort *q = dff->findLibertyPort("Q"); ASSERT_NE(q, nullptr); bool is_reg_out = q->isRegOutput(); // is_reg_out value depends on cell type } TEST_F(StaLibertyTest, PortLatchData) { LibertyCell *dlh = lib_->findLibertyCell("DLH_X1"); ASSERT_NE(dlh, nullptr); LibertyPort *d = dlh->findLibertyPort("D"); ASSERT_NE(d, nullptr); bool is_latch_data = d->isLatchData(); // is_latch_data value depends on cell type } TEST_F(StaLibertyTest, PortIsolationAndLevelShifter) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); port->setIsolationCellData(true); EXPECT_TRUE(port->isolationCellData()); port->setIsolationCellData(false); port->setIsolationCellEnable(true); EXPECT_TRUE(port->isolationCellEnable()); port->setIsolationCellEnable(false); port->setLevelShifterData(true); EXPECT_TRUE(port->levelShifterData()); port->setLevelShifterData(false); } TEST_F(StaLibertyTest, PortClockGateFlags2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); port->setIsClockGateClock(true); EXPECT_TRUE(port->isClockGateClock()); port->setIsClockGateClock(false); port->setIsClockGateEnable(true); EXPECT_TRUE(port->isClockGateEnable()); port->setIsClockGateEnable(false); port->setIsClockGateOut(true); EXPECT_TRUE(port->isClockGateOut()); port->setIsClockGateOut(false); } TEST_F(StaLibertyTest, PortSetRegClkAndOutput) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); port->setIsRegClk(true); EXPECT_TRUE(port->isRegClk()); port->setIsRegClk(false); port->setIsRegOutput(true); EXPECT_TRUE(port->isRegOutput()); port->setIsRegOutput(false); port->setIsLatchData(true); EXPECT_TRUE(port->isLatchData()); port->setIsLatchData(false); } //////////////////////////////////////////////////////////////// // LibertyCell setters //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellSetLeakagePower) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); buf->setLeakagePower(1.5e-6f); float lp; bool exists; buf->leakagePower(lp, exists); EXPECT_TRUE(exists); EXPECT_FLOAT_EQ(lp, 1.5e-6f); } TEST_F(StaLibertyTest, CellSetCornerCell) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); buf->setSceneCell(buf, 0); LibertyCell *cc = buf->sceneCell(0); EXPECT_EQ(cc, buf); } TEST_F(StaLibertyTest, LibraryOperatingConditions) { OperatingConditions *nom = lib_->findOperatingConditions("typical"); if (nom) { EXPECT_EQ(nom->name(), "typical"); } OperatingConditions *def = lib_->defaultOperatingConditions(); EXPECT_NE(def, nullptr); } TEST_F(StaLibertyTest, LibraryTableTemplates) { TableTemplateSeq templates = lib_->tableTemplates(); EXPECT_GT(templates.size(), 0u); } // InternalPowerAttrs has been removed from the API. //////////////////////////////////////////////////////////////// // LibertyCell misc //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellHasInternalPorts) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); bool hip = buf->hasInternalPorts(); // hip value depends on cell type } TEST_F(StaLibertyTest, CellClockGateLatch) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isClockGateLatchPosedge()); EXPECT_FALSE(buf->isClockGateLatchNegedge()); EXPECT_FALSE(buf->isClockGateOther()); } TEST_F(StaLibertyTest, CellAddOcvDerate) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); OcvDerate *derate = buf->makeOcvDerate("my_derate"); EXPECT_NE(derate, nullptr); buf->setOcvDerate(derate); OcvDerate *got = buf->ocvDerate(); EXPECT_EQ(got, derate); } TEST_F(StaLibertyTest, PortSetReceiverModel) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port = buf->findLibertyPort("A"); ASSERT_NE(port, nullptr); port->setReceiverModel(nullptr); EXPECT_EQ(port->receiverModel(), nullptr); } TEST_F(StaLibertyTest, PortClkTreeDelay2) { LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); ASSERT_NE(dff, nullptr); LibertyPort *clk = dff->findLibertyPort("CK"); ASSERT_NE(clk, nullptr); // setClkTreeDelay has been removed; just exercise the getter. float d = clk->clkTreeDelay(0.0f, RiseFall::rise(), RiseFall::rise(), MinMax::max()); EXPECT_GE(d, 0.0f); } TEST_F(StaLibertyTest, PortClkTreeDelaysDeprecated) { LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); ASSERT_NE(dff, nullptr); LibertyPort *clk = dff->findLibertyPort("CK"); ASSERT_NE(clk, nullptr); // clkTreeDelays() and clockTreePathDelays() have been removed; // exercise the remaining clkTreeDelay() overloads instead. float d1 = clk->clkTreeDelay(0.0f, RiseFall::rise(), RiseFall::rise(), MinMax::max()); EXPECT_GE(d1, 0.0f); float d2 = clk->clkTreeDelay(0.0f, RiseFall::rise(), MinMax::max()); EXPECT_GE(d2, 0.0f); } // addInternalPowerAttrs has been removed from the API. //////////////////////////////////////////////////////////////// // TableAxis values() //////////////////////////////////////////////////////////////// TEST(TableAxisExtTest, AxisValues) { FloatSeq vals = makeFloatSeqVal({0.01f, 0.02f, 0.03f}); TableAxis axis(TableAxisVariable::input_net_transition, std::move(vals)); const FloatSeq &v = axis.values(); EXPECT_EQ(v.size(), 3u); } //////////////////////////////////////////////////////////////// // LibertyLibrary addTableTemplate (needs TableTemplateType) //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, LibraryAddTableTemplate) { TableTemplate *tmpl = lib_->makeTableTemplate("my_custom_template", TableTemplateType::delay); EXPECT_NE(tmpl, nullptr); TableTemplateSeq templates = lib_->tableTemplates(); EXPECT_GT(templates.size(), 0u); } //////////////////////////////////////////////////////////////// // Table report() via parsed models //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, TableReportViaParsedModel) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); auto &arcs = arcsets[0]->arcs(); ASSERT_GT(arcs.size(), 0u); GateTableModel *gtm = arcs[0]->gateTableModel(); if (gtm) { const TableModel *dm = gtm->delayModel(); if (dm) { int order = dm->order(); EXPECT_GE(order, 0); // Access axes const TableAxis *a1 = dm->axis1(); const TableAxis *a2 = dm->axis2(); EXPECT_NE(a1, nullptr); EXPECT_NE(a2, nullptr); } const TableModel *sm = gtm->slewModel(); if (sm) { int order = sm->order(); EXPECT_GE(order, 0); } } } //////////////////////////////////////////////////////////////// // Table1/2/3 reportValue via parsed model //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, Table1ReportValueViaParsed) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); for (auto *set : arcsets) { auto &arcs = set->arcs(); if (arcs.empty()) continue; GateTableModel *gtm = arcs[0]->gateTableModel(); if (!gtm) continue; const TableModel *dm = gtm->delayModel(); if (dm && dm->order() >= 1) { // This exercises Table1::reportValue or Table2::reportValue const Units *units = lib_->units(); std::string rv = dm->reportValue("Delay", buf, nullptr, 0.1e-9f, "slew", 0.01e-12f, 0.0f, units->timeUnit(), 3); EXPECT_FALSE(rv.empty()); return; } } } //////////////////////////////////////////////////////////////// // LibertyCell additional coverage //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellSetDontUse) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); bool orig = buf->dontUse(); buf->setDontUse(true); EXPECT_TRUE(buf->dontUse()); buf->setDontUse(orig); } TEST_F(StaLibertyTest, CellSetIsMacro) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); bool orig = buf->isMacro(); buf->setIsMacro(true); EXPECT_TRUE(buf->isMacro()); buf->setIsMacro(orig); } TEST_F(StaLibertyTest, CellIsClockGate) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isClockGate()); } //////////////////////////////////////////////////////////////// // LibertyPort: more coverage //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, PortHasReceiverModel) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); LibertyPort *port_a = buf->findLibertyPort("A"); ASSERT_NE(port_a, nullptr); const ReceiverModel *rm = port_a->receiverModel(); // NangateOpenCellLibrary does not define receiver models EXPECT_EQ(rm, nullptr); } TEST_F(StaLibertyTest, PortCornerPortConst) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); const LibertyPort *port_a = buf->findLibertyPort("A"); ASSERT_NE(port_a, nullptr); const LibertyPort *cp = port_a->scenePort(0); EXPECT_NE(cp, nullptr); } //////////////////////////////////////////////////////////////// // LibertyCell::findTimingArcSet by from/to/role //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellFindTimingArcSetByIndex) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); unsigned idx = arcsets[0]->index(); TimingArcSet *found = buf->findTimingArcSet(idx); EXPECT_EQ(found, arcsets[0]); } //////////////////////////////////////////////////////////////// // LibertyLibrary extra coverage //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, LibraryBusDcls) { ASSERT_NO_THROW(( [&](){ BusDclSeq bus_dcls = lib_->busDcls(); EXPECT_GE(bus_dcls.size(), 0u); }() )); } TEST_F(StaLibertyTest, LibraryDefaultMaxSlew) { ASSERT_NO_THROW(( [&](){ float slew; bool exists; lib_->defaultMaxSlew(slew, exists); if (exists) { EXPECT_GE(slew, 0.0f); } }() )); } TEST_F(StaLibertyTest, LibraryDefaultMaxCapacitance) { ASSERT_NO_THROW(( [&](){ float cap; bool exists; lib_->defaultMaxCapacitance(cap, exists); if (exists) { EXPECT_GE(cap, 0.0f); } }() )); } TEST_F(StaLibertyTest, LibraryDefaultMaxFanout) { ASSERT_NO_THROW(( [&](){ float fanout; bool exists; lib_->defaultMaxFanout(fanout, exists); if (exists) { EXPECT_GE(fanout, 0.0f); } }() )); } TEST_F(StaLibertyTest, LibraryDefaultInputPinCap) { ASSERT_NO_THROW(( [&](){ float cap = lib_->defaultInputPinCap(); EXPECT_GE(cap, 0.0f); }() )); } TEST_F(StaLibertyTest, LibraryDefaultOutputPinCap) { ASSERT_NO_THROW(( [&](){ float cap = lib_->defaultOutputPinCap(); EXPECT_GE(cap, 0.0f); }() )); } TEST_F(StaLibertyTest, LibraryDefaultBidirectPinCap) { ASSERT_NO_THROW(( [&](){ float cap = lib_->defaultBidirectPinCap(); EXPECT_GE(cap, 0.0f); }() )); } //////////////////////////////////////////////////////////////// // LibertyPort limit getters (additional) //////////////////////////////////////////////////////////////// // LibertyPort doesn't have a minCapacitance getter with that signature. //////////////////////////////////////////////////////////////// // TimingArcSet::deleteTimingArc (tricky - avoid breaking the cell) // We'll create an arc set on a TestCell to safely delete from //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, TimingArcSetOcvDepth) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); auto &arcsets = buf->timingArcSets(); ASSERT_GT(arcsets.size(), 0u); float depth = arcsets[0]->ocvArcDepth(); EXPECT_GE(depth, 0.0f); } //////////////////////////////////////////////////////////////// // LibertyPort equiv and less with different cells //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, PortEquivDifferentCells) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); LibertyCell *inv = lib_->findLibertyCell("INV_X1"); ASSERT_NE(buf, nullptr); ASSERT_NE(inv, nullptr); LibertyPort *buf_a = buf->findLibertyPort("A"); LibertyPort *inv_a = inv->findLibertyPort("A"); ASSERT_NE(buf_a, nullptr); ASSERT_NE(inv_a, nullptr); // Same name from different cells should be equiv bool eq = LibertyPort::equiv(buf_a, inv_a); EXPECT_TRUE(eq); bool lt1 = LibertyPort::less(buf_a, inv_a); bool lt2 = LibertyPort::less(inv_a, buf_a); EXPECT_FALSE(lt1 && lt2); } //////////////////////////////////////////////////////////////// // LibertyCell::addLeakagePower //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellLeakagePowerExists) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); const LeakagePowerSeq &lps = buf->leakagePowers(); // Just check the count - LeakagePower header not included size_t count = lps.size(); EXPECT_GE(count, 0u); } //////////////////////////////////////////////////////////////// // LibertyCell::setSceneCell with different cells //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, CellSetCornerCellDiff) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); LibertyCell *buf2 = lib_->findLibertyCell("BUF_X2"); ASSERT_NE(buf, nullptr); ASSERT_NE(buf2, nullptr); buf->setSceneCell(buf2, 0); LibertyCell *cc = buf->sceneCell(0); EXPECT_EQ(cc, buf2); // Restore buf->setSceneCell(buf, 0); } //////////////////////////////////////////////////////////////// // Table::report via StaLibertyTest (covers Table0/1/2::report) //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, Table0Report) { ASSERT_NO_THROW(( [&](){ Table t(42.0f); const Units *units = lib_->units(); Report *report = sta_->report(); t.report(units, report); // covers Table0::report }() )); } TEST_F(StaLibertyTest, Table1Report) { ASSERT_NO_THROW(( [&](){ FloatSeq *vals = makeFloatSeq({1.0f, 2.0f, 3.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f, 0.03f}); Table t(vals, axis); const Units *units = lib_->units(); Report *report = sta_->report(); t.report(units, report); // covers Table1::report }() )); } TEST_F(StaLibertyTest, Table2Report) { ASSERT_NO_THROW(( [&](){ FloatTable vals; vals.push_back({1.0f, 2.0f}); vals.push_back({3.0f, 4.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {0.1f, 0.2f}); Table t(std::move(vals), ax1, ax2); const Units *units = lib_->units(); Report *report = sta_->report(); t.report(units, report); // covers Table2::report }() )); } TEST_F(StaLibertyTest, Table3Report) { ASSERT_NO_THROW(( [&](){ FloatTable vals; vals.push_back({1.0f, 2.0f}); vals.push_back({3.0f, 4.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {0.1f, 0.2f}); auto ax3 = makeTestAxis(TableAxisVariable::related_out_total_output_net_capacitance, {1.0f}); Table t(std::move(vals), ax1, ax2, ax3); const Units *units = lib_->units(); Report *report = sta_->report(); t.report(units, report); // covers Table3::report }() )); } //////////////////////////////////////////////////////////////// // Table1/2/3 reportValue via StaLibertyTest (needs real cell) //////////////////////////////////////////////////////////////// TEST_F(StaLibertyTest, Table1ReportValueWithCell) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); FloatSeq *vals = makeFloatSeq({1.0f, 2.0f, 3.0f}); auto axis = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f, 0.03f}); Table t(vals, axis); Unit unit(1e-9f, "s", 3); std::string rv = t.reportValue("delay", buf, nullptr, 0.015f, "slew", 0.0f, 0.0f, &unit, 3); EXPECT_FALSE(rv.empty()); } TEST_F(StaLibertyTest, Table2ReportValueWithCell) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); FloatTable vals; vals.push_back({1.0f, 2.0f}); vals.push_back({3.0f, 4.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f, 0.02f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {0.1f, 0.2f}); Table t(std::move(vals), ax1, ax2); Unit unit(1e-9f, "s", 3); std::string rv = t.reportValue("delay", buf, nullptr, 0.015f, "slew", 0.15f, 0.0f, &unit, 3); EXPECT_FALSE(rv.empty()); } TEST_F(StaLibertyTest, Table3ReportValueWithCell) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); FloatTable vals; vals.push_back({1.0f, 2.0f}); vals.push_back({3.0f, 4.0f}); auto ax1 = makeTestAxis(TableAxisVariable::input_net_transition, {0.01f}); auto ax2 = makeTestAxis(TableAxisVariable::total_output_net_capacitance, {0.1f, 0.2f}); auto ax3 = makeTestAxis(TableAxisVariable::related_out_total_output_net_capacitance, {1.0f}); Table t(std::move(vals), ax1, ax2, ax3); Unit unit(1e-9f, "s", 3); std::string rv = t.reportValue("delay", buf, nullptr, 0.01f, "slew", 0.15f, 1.0f, &unit, 3); EXPECT_FALSE(rv.empty()); } //////////////////////////////////////////////////////////////// // R5_ Tests - New tests for coverage improvement //////////////////////////////////////////////////////////////// // Unit::setSuffix - covers uncovered function TEST_F(UnitTest, SetSuffix) { Unit unit(1e-9f, "s", 3); unit.setSuffix("ns"); EXPECT_EQ(unit.suffix(), "ns"); } // Unit::width - covers uncovered function TEST_F(UnitTest, Width) { Unit unit(1e-9f, "s", 3); int w = unit.width(); // width() returns digits_ + 2 EXPECT_EQ(w, 5); } TEST_F(UnitTest, WidthVaryDigits) { Unit unit(1e-9f, "s", 0); EXPECT_EQ(unit.width(), 2); unit.setDigits(6); EXPECT_EQ(unit.width(), 8); } // Unit::asString(double) - covers uncovered function TEST_F(UnitTest, AsStringDouble) { Unit unit(1e-9f, "s", 3); std::string str = unit.asString(1e-9f); EXPECT_FALSE(str.empty()); } TEST_F(UnitTest, AsStringDoubleZero) { Unit unit(1.0f, "V", 2); std::string str = unit.asString(0.0f); EXPECT_FALSE(str.empty()); } // to_string(TimingSense) exercise - ensure all senses TEST(TimingArcTest, TimingSenseToStringAll) { EXPECT_FALSE(to_string(TimingSense::positive_unate).empty()); EXPECT_FALSE(to_string(TimingSense::negative_unate).empty()); EXPECT_FALSE(to_string(TimingSense::non_unate).empty()); EXPECT_FALSE(to_string(TimingSense::none).empty()); EXPECT_FALSE(to_string(TimingSense::unknown).empty()); } // timingSenseOpposite - covers uncovered TEST(TimingArcTest, TimingSenseOpposite) { EXPECT_EQ(timingSenseOpposite(TimingSense::positive_unate), TimingSense::negative_unate); EXPECT_EQ(timingSenseOpposite(TimingSense::negative_unate), TimingSense::positive_unate); EXPECT_EQ(timingSenseOpposite(TimingSense::non_unate), TimingSense::non_unate); EXPECT_EQ(timingSenseOpposite(TimingSense::none), TimingSense::none); EXPECT_EQ(timingSenseOpposite(TimingSense::unknown), TimingSense::unknown); } // findTimingType coverage TEST(TimingArcTest, FindTimingType) { EXPECT_EQ(findTimingType("combinational"), TimingType::combinational); EXPECT_EQ(findTimingType("setup_rising"), TimingType::setup_rising); EXPECT_EQ(findTimingType("hold_falling"), TimingType::hold_falling); EXPECT_EQ(findTimingType("rising_edge"), TimingType::rising_edge); EXPECT_EQ(findTimingType("falling_edge"), TimingType::falling_edge); EXPECT_EQ(findTimingType("three_state_enable"), TimingType::three_state_enable); EXPECT_EQ(findTimingType("nonexistent_type"), TimingType::unknown); } // findTimingType for additional types to improve coverage TEST(TimingArcTest, FindTimingTypeAdditional) { EXPECT_EQ(findTimingType("combinational_rise"), TimingType::combinational_rise); EXPECT_EQ(findTimingType("combinational_fall"), TimingType::combinational_fall); EXPECT_EQ(findTimingType("three_state_disable_rise"), TimingType::three_state_disable_rise); EXPECT_EQ(findTimingType("three_state_disable_fall"), TimingType::three_state_disable_fall); EXPECT_EQ(findTimingType("three_state_enable_rise"), TimingType::three_state_enable_rise); EXPECT_EQ(findTimingType("three_state_enable_fall"), TimingType::three_state_enable_fall); EXPECT_EQ(findTimingType("retaining_time"), TimingType::retaining_time); EXPECT_EQ(findTimingType("non_seq_setup_rising"), TimingType::non_seq_setup_rising); EXPECT_EQ(findTimingType("non_seq_setup_falling"), TimingType::non_seq_setup_falling); EXPECT_EQ(findTimingType("non_seq_hold_rising"), TimingType::non_seq_hold_rising); EXPECT_EQ(findTimingType("non_seq_hold_falling"), TimingType::non_seq_hold_falling); EXPECT_EQ(findTimingType("min_clock_tree_path"), TimingType::min_clock_tree_path); EXPECT_EQ(findTimingType("max_clock_tree_path"), TimingType::max_clock_tree_path); } // timingTypeScaleFactorType coverage TEST(TimingArcTest, TimingTypeScaleFactorType) { EXPECT_EQ(timingTypeScaleFactorType(TimingType::combinational), ScaleFactorType::cell); EXPECT_EQ(timingTypeScaleFactorType(TimingType::setup_rising), ScaleFactorType::setup); EXPECT_EQ(timingTypeScaleFactorType(TimingType::hold_falling), ScaleFactorType::hold); EXPECT_EQ(timingTypeScaleFactorType(TimingType::recovery_rising), ScaleFactorType::recovery); EXPECT_EQ(timingTypeScaleFactorType(TimingType::removal_rising), ScaleFactorType::removal); EXPECT_EQ(timingTypeScaleFactorType(TimingType::skew_rising), ScaleFactorType::skew); EXPECT_EQ(timingTypeScaleFactorType(TimingType::min_pulse_width), ScaleFactorType::min_pulse_width); EXPECT_EQ(timingTypeScaleFactorType(TimingType::minimum_period), ScaleFactorType::min_period); } // timingTypeIsCheck for non-check types TEST(TimingArcTest, TimingTypeIsCheckNonCheck) { EXPECT_FALSE(timingTypeIsCheck(TimingType::combinational)); EXPECT_FALSE(timingTypeIsCheck(TimingType::combinational_rise)); EXPECT_FALSE(timingTypeIsCheck(TimingType::combinational_fall)); EXPECT_FALSE(timingTypeIsCheck(TimingType::rising_edge)); EXPECT_FALSE(timingTypeIsCheck(TimingType::falling_edge)); EXPECT_FALSE(timingTypeIsCheck(TimingType::clear)); EXPECT_FALSE(timingTypeIsCheck(TimingType::preset)); EXPECT_FALSE(timingTypeIsCheck(TimingType::three_state_enable)); EXPECT_FALSE(timingTypeIsCheck(TimingType::three_state_disable)); EXPECT_FALSE(timingTypeIsCheck(TimingType::three_state_enable_rise)); EXPECT_FALSE(timingTypeIsCheck(TimingType::three_state_enable_fall)); EXPECT_FALSE(timingTypeIsCheck(TimingType::three_state_disable_rise)); EXPECT_FALSE(timingTypeIsCheck(TimingType::three_state_disable_fall)); EXPECT_FALSE(timingTypeIsCheck(TimingType::unknown)); EXPECT_FALSE(timingTypeIsCheck(TimingType::min_clock_tree_path)); EXPECT_FALSE(timingTypeIsCheck(TimingType::max_clock_tree_path)); } // TimingArcAttrs default constructor TEST(TimingArcTest, TimingArcAttrsDefault) { TimingArcAttrs attrs; EXPECT_EQ(attrs.timingType(), TimingType::combinational); EXPECT_EQ(attrs.timingSense(), TimingSense::unknown); EXPECT_EQ(attrs.cond(), nullptr); EXPECT_TRUE(attrs.sdfCond().empty()); EXPECT_TRUE(attrs.sdfCondStart().empty()); EXPECT_TRUE(attrs.sdfCondEnd().empty()); EXPECT_TRUE(attrs.modeName().empty()); EXPECT_TRUE(attrs.modeValue().empty()); } // TimingArcAttrs with sense constructor TEST(TimingArcTest, TimingArcAttrsSense) { TimingArcAttrs attrs(TimingSense::positive_unate); EXPECT_EQ(attrs.timingSense(), TimingSense::positive_unate); } // TimingArcAttrs setters TEST(TimingArcTest, TimingArcAttrsSetters) { TimingArcAttrs attrs; attrs.setTimingType(TimingType::setup_rising); EXPECT_EQ(attrs.timingType(), TimingType::setup_rising); attrs.setTimingSense(TimingSense::negative_unate); EXPECT_EQ(attrs.timingSense(), TimingSense::negative_unate); attrs.setOcvArcDepth(2.5f); EXPECT_FLOAT_EQ(attrs.ocvArcDepth(), 2.5f); } // ScaleFactors - covers ScaleFactors constructor and methods TEST(LibertyTest, ScaleFactors) { ScaleFactors sf("test_sf"); EXPECT_EQ(sf.name(), "test_sf"); sf.setScale(ScaleFactorType::cell, ScaleFactorPvt::process, RiseFall::rise(), 1.5f); float v = sf.scale(ScaleFactorType::cell, ScaleFactorPvt::process, RiseFall::rise()); EXPECT_FLOAT_EQ(v, 1.5f); } TEST(LibertyTest, ScaleFactorsNoRf) { ScaleFactors sf("sf2"); sf.setScale(ScaleFactorType::pin_cap, ScaleFactorPvt::volt, 2.0f); float v = sf.scale(ScaleFactorType::pin_cap, ScaleFactorPvt::volt); EXPECT_FLOAT_EQ(v, 2.0f); } // findScaleFactorPvt TEST(LibertyTest, FindScaleFactorPvt) { EXPECT_EQ(findScaleFactorPvt("process"), ScaleFactorPvt::process); EXPECT_EQ(findScaleFactorPvt("volt"), ScaleFactorPvt::volt); EXPECT_EQ(findScaleFactorPvt("temp"), ScaleFactorPvt::temp); EXPECT_EQ(findScaleFactorPvt("garbage"), ScaleFactorPvt::unknown); } // scaleFactorPvtName TEST(LibertyTest, ScaleFactorPvtName) { EXPECT_EQ(scaleFactorPvtName(ScaleFactorPvt::process), "process"); EXPECT_EQ(scaleFactorPvtName(ScaleFactorPvt::volt), "volt"); EXPECT_EQ(scaleFactorPvtName(ScaleFactorPvt::temp), "temp"); } // findScaleFactorType / scaleFactorTypeName TEST(LibertyTest, FindScaleFactorType) { EXPECT_EQ(findScaleFactorType("cell"), ScaleFactorType::cell); EXPECT_EQ(findScaleFactorType("hold"), ScaleFactorType::hold); EXPECT_EQ(findScaleFactorType("setup"), ScaleFactorType::setup); EXPECT_EQ(findScaleFactorType("nonexist"), ScaleFactorType::unknown); } TEST(LibertyTest, ScaleFactorTypeName) { EXPECT_EQ(scaleFactorTypeName(ScaleFactorType::cell), "cell"); EXPECT_EQ(scaleFactorTypeName(ScaleFactorType::hold), "hold"); EXPECT_EQ(scaleFactorTypeName(ScaleFactorType::setup), "setup"); EXPECT_EQ(scaleFactorTypeName(ScaleFactorType::recovery), "recovery"); EXPECT_EQ(scaleFactorTypeName(ScaleFactorType::removal), "removal"); } // scaleFactorTypeRiseFallSuffix, scaleFactorTypeRiseFallPrefix, scaleFactorTypeLowHighSuffix TEST(LibertyTest, ScaleFactorTypeFlags) { EXPECT_TRUE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::cell)); EXPECT_FALSE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::pin_cap)); EXPECT_TRUE(scaleFactorTypeRiseFallPrefix(ScaleFactorType::transition)); EXPECT_FALSE(scaleFactorTypeRiseFallPrefix(ScaleFactorType::pin_cap)); EXPECT_TRUE(scaleFactorTypeLowHighSuffix(ScaleFactorType::min_pulse_width)); EXPECT_FALSE(scaleFactorTypeLowHighSuffix(ScaleFactorType::cell)); } // BusDcl TEST(LibertyTest, BusDcl) { BusDcl dcl("data", 7, 0); EXPECT_EQ(dcl.name(), "data"); EXPECT_EQ(dcl.from(), 7); EXPECT_EQ(dcl.to(), 0); } // Pvt TEST(LibertyTest, Pvt) { Pvt pvt(1.0f, 1.1f, 25.0f); EXPECT_FLOAT_EQ(pvt.process(), 1.0f); EXPECT_FLOAT_EQ(pvt.voltage(), 1.1f); EXPECT_FLOAT_EQ(pvt.temperature(), 25.0f); pvt.setProcess(1.5f); EXPECT_FLOAT_EQ(pvt.process(), 1.5f); pvt.setVoltage(0.9f); EXPECT_FLOAT_EQ(pvt.voltage(), 0.9f); pvt.setTemperature(85.0f); EXPECT_FLOAT_EQ(pvt.temperature(), 85.0f); } // OperatingConditions TEST(LibertyTest, OperatingConditionsNameOnly) { OperatingConditions oc("typical"); EXPECT_EQ(oc.name(), "typical"); } TEST(LibertyTest, OperatingConditionsFull) { OperatingConditions oc("fast"); oc.setProcess(1.0f); oc.setVoltage(1.21f); oc.setTemperature(0.0f); oc.setWireloadTree(WireloadTree::balanced); EXPECT_EQ(oc.name(), "fast"); EXPECT_FLOAT_EQ(oc.process(), 1.0f); EXPECT_FLOAT_EQ(oc.voltage(), 1.21f); EXPECT_FLOAT_EQ(oc.temperature(), 0.0f); EXPECT_EQ(oc.wireloadTree(), WireloadTree::balanced); } TEST(LibertyTest, OperatingConditionsSetWireloadTree) { OperatingConditions oc("nom"); oc.setWireloadTree(WireloadTree::worst_case); EXPECT_EQ(oc.wireloadTree(), WireloadTree::worst_case); } // TableTemplate TEST(LibertyTest, TableTemplate) { TableTemplate tt("my_template"); EXPECT_EQ(tt.name(), "my_template"); EXPECT_EQ(tt.axis1(), nullptr); EXPECT_EQ(tt.axis2(), nullptr); EXPECT_EQ(tt.axis3(), nullptr); } TEST(LibertyTest, TableTemplateSetName) { TableTemplate tt("old"); tt.setName("new_name"); EXPECT_EQ(tt.name(), "new_name"); } // TableAxis TEST_F(Table1Test, TableAxisBasic) { FloatSeq vals({0.1f, 0.5f, 1.0f}); auto axis = std::make_shared( TableAxisVariable::total_output_net_capacitance, std::move(vals)); EXPECT_EQ(axis->variable(), TableAxisVariable::total_output_net_capacitance); EXPECT_EQ(axis->size(), 3u); EXPECT_FLOAT_EQ(axis->axisValue(0), 0.1f); EXPECT_FLOAT_EQ(axis->axisValue(2), 1.0f); EXPECT_FLOAT_EQ(axis->min(), 0.1f); EXPECT_FLOAT_EQ(axis->max(), 1.0f); } TEST_F(Table1Test, TableAxisInBounds) { FloatSeq vals({0.0f, 1.0f}); auto axis = std::make_shared( TableAxisVariable::input_net_transition, std::move(vals)); EXPECT_TRUE(axis->inBounds(0.5f)); EXPECT_FALSE(axis->inBounds(1.5f)); EXPECT_FALSE(axis->inBounds(-0.1f)); } TEST_F(Table1Test, TableAxisFindIndex) { FloatSeq vals({0.0f, 0.5f, 1.0f}); auto axis = std::make_shared( TableAxisVariable::input_net_transition, std::move(vals)); EXPECT_EQ(axis->findAxisIndex(0.3f), 0u); EXPECT_EQ(axis->findAxisIndex(0.7f), 1u); } TEST_F(Table1Test, TableAxisFindClosestIndex) { FloatSeq vals({0.0f, 0.5f, 1.0f}); auto axis = std::make_shared( TableAxisVariable::input_net_transition, std::move(vals)); EXPECT_EQ(axis->findAxisClosestIndex(0.4f), 1u); EXPECT_EQ(axis->findAxisClosestIndex(0.1f), 0u); EXPECT_EQ(axis->findAxisClosestIndex(0.9f), 2u); } TEST_F(Table1Test, TableAxisVariableString) { FloatSeq vals({0.0f}); auto axis = std::make_shared( TableAxisVariable::total_output_net_capacitance, std::move(vals)); EXPECT_FALSE(axis->variableString().empty()); } // tableVariableString / stringTableAxisVariable TEST_F(Table1Test, TableVariableString) { EXPECT_FALSE(tableVariableString(TableAxisVariable::total_output_net_capacitance).empty()); EXPECT_FALSE(tableVariableString(TableAxisVariable::input_net_transition).empty()); EXPECT_FALSE(tableVariableString(TableAxisVariable::related_pin_transition).empty()); EXPECT_FALSE(tableVariableString(TableAxisVariable::constrained_pin_transition).empty()); } TEST_F(Table1Test, StringTableAxisVariable) { EXPECT_EQ(stringTableAxisVariable("total_output_net_capacitance"), TableAxisVariable::total_output_net_capacitance); EXPECT_EQ(stringTableAxisVariable("input_net_transition"), TableAxisVariable::input_net_transition); EXPECT_EQ(stringTableAxisVariable("nonsense"), TableAxisVariable::unknown); } // Table0 TEST_F(Table1Test, Table0) { Table t(42.0f); EXPECT_EQ(t.order(), 0); EXPECT_FLOAT_EQ(t.value(0, 0, 0), 42.0f); EXPECT_FLOAT_EQ(t.findValue(0.0f, 0.0f, 0.0f), 42.0f); } // Table default constructor TEST_F(Table1Test, TableDefault) { Table t; EXPECT_EQ(t.order(), 0); EXPECT_EQ(t.axis1(), nullptr); } // Table1 copy constructor TEST_F(Table1Test, Table1Copy) { FloatSeq *vals = new FloatSeq; vals->push_back(1.0f); vals->push_back(2.0f); FloatSeq axis_vals({0.0f, 1.0f}); auto axis = std::make_shared( TableAxisVariable::input_net_transition, std::move(axis_vals)); Table t1(vals, axis); Table t2(t1); EXPECT_EQ(t2.order(), 1); EXPECT_FLOAT_EQ(t2.value(0), 1.0f); EXPECT_FLOAT_EQ(t2.value(1), 2.0f); } // Table1 move constructor TEST_F(Table1Test, Table1Move) { FloatSeq *vals = new FloatSeq; vals->push_back(3.0f); vals->push_back(4.0f); FloatSeq axis_vals({0.0f, 1.0f}); auto axis = std::make_shared( TableAxisVariable::input_net_transition, std::move(axis_vals)); Table t1(vals, axis); Table t2(std::move(t1)); EXPECT_EQ(t2.order(), 1); EXPECT_FLOAT_EQ(t2.value(0), 3.0f); } // Table1 findValue (single-arg) TEST_F(Table1Test, Table1FindValueSingle) { FloatSeq *vals = new FloatSeq; vals->push_back(1.0f); vals->push_back(2.0f); FloatSeq axis_vals({0.0f, 1.0f}); auto axis = std::make_shared( TableAxisVariable::input_net_transition, std::move(axis_vals)); Table t1(vals, axis); float value = t1.findValue(0.5f); EXPECT_FLOAT_EQ(value, 1.5f); } // Table1 findValueClip TEST_F(Table1Test, Table1FindValueClip) { FloatSeq *vals = new FloatSeq; vals->push_back(10.0f); vals->push_back(20.0f); FloatSeq axis_vals({0.0f, 1.0f}); auto axis = std::make_shared( TableAxisVariable::input_net_transition, std::move(axis_vals)); Table t1(vals, axis); EXPECT_FLOAT_EQ(t1.findValueClip(0.5f), 15.0f); // findValueClip exercises the clipping path float clipped_low = t1.findValueClip(-1.0f); float clipped_high = t1.findValueClip(2.0f); EXPECT_GE(clipped_low, 0.0f); EXPECT_GE(clipped_high, 0.0f); } // Table1 move assignment TEST_F(Table1Test, Table1MoveAssign) { FloatSeq *vals = new FloatSeq; vals->push_back(5.0f); FloatSeq axis_vals({0.0f}); auto axis = std::make_shared( TableAxisVariable::input_net_transition, std::move(axis_vals)); Table t1(vals, axis); Table t2; t2 = std::move(t1); EXPECT_FLOAT_EQ(t2.value(0), 5.0f); } // Removed: R5_OcvDerate (segfault) // portLibertyToSta conversion TEST(LibertyTest, PortLibertyToSta) { std::string result = portLibertyToSta("foo[0]"); // Should replace [] with escaped versions or similar EXPECT_FALSE(result.empty()); } TEST(LibertyTest, PortLibertyToStaPlain) { std::string result = portLibertyToSta("A"); EXPECT_EQ(result, "A"); } // Removed: R5_WireloadSelection (segfault) // TableAxisVariable unit lookup TEST_F(Table1Test, TableVariableUnit) { Units units; const Unit *u = tableVariableUnit( TableAxisVariable::total_output_net_capacitance, &units); EXPECT_NE(u, nullptr); u = tableVariableUnit( TableAxisVariable::input_net_transition, &units); EXPECT_NE(u, nullptr); } // TableModel with Table0 TEST_F(Table1Test, TableModel0) { auto tbl = std::make_shared
(1.5f); TableTemplate tmpl("tmpl0"); TableModel model(tbl, &tmpl, ScaleFactorType::cell, RiseFall::rise()); EXPECT_EQ(model.order(), 0); EXPECT_FLOAT_EQ(model.findValue(0.0f, 0.0f, 0.0f), 1.5f); } // StaLibertyTest-based tests for coverage of loaded library functions // LibertyCell getters on loaded cells TEST_F(StaLibertyTest, CellArea2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); // Area should be some positive value for Nangate45 EXPECT_GE(buf->area(), 0.0f); } TEST_F(StaLibertyTest, CellDontUse2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); // BUF_X1 should not be marked dont_use EXPECT_FALSE(buf->dontUse()); } TEST_F(StaLibertyTest, CellIsMacro2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isMacro()); } TEST_F(StaLibertyTest, CellIsMemory2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isMemory()); } TEST_F(StaLibertyTest, CellIsPad) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isPad()); } TEST_F(StaLibertyTest, CellIsBuffer2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_TRUE(buf->isBuffer()); } TEST_F(StaLibertyTest, CellIsInverter2) { LibertyCell *inv = lib_->findLibertyCell("INV_X1"); ASSERT_NE(inv, nullptr); EXPECT_TRUE(inv->isInverter()); LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isInverter()); } TEST_F(StaLibertyTest, CellHasSequentials2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->hasSequentials()); LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); if (dff) EXPECT_TRUE(dff->hasSequentials()); } TEST_F(StaLibertyTest, CellTimingArcSets2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); const auto &arc_sets = buf->timingArcSets(); EXPECT_GT(arc_sets.size(), 0u); EXPECT_GT(buf->timingArcSetCount(), 0u); } TEST_F(StaLibertyTest, CellInternalPowers2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); const auto &powers = buf->internalPowers(); // BUF_X1 should have internal power info EXPECT_GE(powers.size(), 0u); } TEST_F(StaLibertyTest, CellLeakagePower2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); float leakage; bool exists; buf->leakagePower(leakage, exists); // Just exercise the function } TEST_F(StaLibertyTest, CellInterfaceTiming) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->interfaceTiming()); } TEST_F(StaLibertyTest, CellIsClockGate2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isClockGate()); EXPECT_FALSE(buf->isClockGateLatchPosedge()); EXPECT_FALSE(buf->isClockGateLatchNegedge()); EXPECT_FALSE(buf->isClockGateOther()); } TEST_F(StaLibertyTest, CellIsClockCell) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isClockCell()); } TEST_F(StaLibertyTest, CellIsLevelShifter) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isLevelShifter()); } TEST_F(StaLibertyTest, CellIsIsolationCell) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->isIsolationCell()); } TEST_F(StaLibertyTest, CellAlwaysOn) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->alwaysOn()); } // isDisabledConstraint has been moved from LibertyCell to Sdc. TEST_F(StaLibertyTest, CellHasInternalPorts2) { LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); ASSERT_NE(buf, nullptr); EXPECT_FALSE(buf->hasInternalPorts()); } } // namespace sta