OpenSTA/liberty/test/cpp/TestLibertyClasses.cc

3845 lines
135 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include <string>
#include <cmath>
#include <atomic>
#include <unistd.h>
#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"
namespace sta {
// Test Unit class
class UnitTest : public ::testing::Test {
protected:
void SetUp() override {}
};
TEST_F(UnitTest, DefaultConstructor) {
Unit unit("s");
// Default scale is 1.0
EXPECT_FLOAT_EQ(unit.scale(), 1.0f);
EXPECT_EQ(unit.suffix(), "s");
}
TEST_F(UnitTest, ParameterizedConstructor) {
Unit unit(1e-9f, "s", 3);
EXPECT_FLOAT_EQ(unit.scale(), 1e-9f);
EXPECT_EQ(unit.suffix(), "s");
EXPECT_EQ(unit.digits(), 3);
}
TEST_F(UnitTest, StaToUser) {
// 1ns scale: internal 1e-9 -> user 1.0
Unit unit(1e-9f, "s", 3);
double result = unit.staToUser(1e-9);
EXPECT_NEAR(result, 1.0, 1e-6);
}
TEST_F(UnitTest, UserToSta) {
Unit unit(1e-9f, "s", 3);
double result = unit.userToSta(1.0);
EXPECT_NEAR(result, 1e-9, 1e-12);
}
TEST_F(UnitTest, AsString) {
Unit unit(1e-9f, "s", 3);
const char *str = unit.asString(1e-9f);
EXPECT_NE(str, nullptr);
// Should produce something like "1.000"
}
TEST_F(UnitTest, SetScale) {
Unit unit("s");
unit.setScale(1e-12f);
EXPECT_FLOAT_EQ(unit.scale(), 1e-12f);
}
TEST_F(UnitTest, SetDigits) {
Unit unit(1.0f, "V", 2);
unit.setDigits(4);
EXPECT_EQ(unit.digits(), 4);
}
// Test Units class
class UnitsTest : public ::testing::Test {
protected:
Units units;
};
TEST_F(UnitsTest, TimeUnit) {
Unit *time = units.timeUnit();
EXPECT_NE(time, nullptr);
EXPECT_EQ(time->suffix(), "s");
}
TEST_F(UnitsTest, CapacitanceUnit) {
Unit *cap = units.capacitanceUnit();
EXPECT_NE(cap, nullptr);
}
TEST_F(UnitsTest, FindTime) {
Unit *found = units.find("time");
EXPECT_NE(found, nullptr);
}
TEST_F(UnitsTest, FindCapacitance) {
Unit *found = units.find("capacitance");
EXPECT_NE(found, nullptr);
}
TEST_F(UnitsTest, FindVoltage) {
Unit *found = units.find("voltage");
EXPECT_NE(found, nullptr);
}
TEST_F(UnitsTest, FindResistance) {
Unit *found = units.find("resistance");
EXPECT_NE(found, nullptr);
}
TEST_F(UnitsTest, FindInvalid) {
Unit *found = units.find("invalid_unit");
EXPECT_EQ(found, nullptr);
}
// Test TimingRole singletons
class TimingRoleTest : public ::testing::Test {};
TEST_F(TimingRoleTest, WireSingleton) {
const TimingRole *wire = TimingRole::wire();
EXPECT_NE(wire, nullptr);
EXPECT_EQ(wire->to_string(), "wire");
}
TEST_F(TimingRoleTest, SetupSingleton) {
const TimingRole *setup = TimingRole::setup();
EXPECT_NE(setup, nullptr);
EXPECT_TRUE(setup->isTimingCheck());
}
TEST_F(TimingRoleTest, HoldSingleton) {
const TimingRole *hold = TimingRole::hold();
EXPECT_NE(hold, nullptr);
EXPECT_TRUE(hold->isTimingCheck());
}
TEST_F(TimingRoleTest, CombinationalSingleton) {
const TimingRole *comb = TimingRole::combinational();
EXPECT_NE(comb, nullptr);
EXPECT_FALSE(comb->isTimingCheck());
}
TEST_F(TimingRoleTest, FindByName) {
const TimingRole *setup = TimingRole::find("setup");
EXPECT_NE(setup, nullptr);
EXPECT_EQ(setup, TimingRole::setup());
}
TEST_F(TimingRoleTest, FindInvalid) {
const TimingRole *invalid = TimingRole::find("nonexistent");
EXPECT_EQ(invalid, nullptr);
}
TEST_F(TimingRoleTest, RegClkToQ) {
const TimingRole *role = TimingRole::regClkToQ();
EXPECT_NE(role, nullptr);
EXPECT_FALSE(role->isTimingCheck());
}
TEST_F(TimingRoleTest, IsWire) {
EXPECT_TRUE(TimingRole::wire()->isWire());
EXPECT_FALSE(TimingRole::setup()->isWire());
}
////////////////////////////////////////////////////////////////
// Wireload string conversion tests - covers wireloadTreeString,
// stringWireloadTree, wireloadModeString, stringWireloadMode
////////////////////////////////////////////////////////////////
TEST(WireloadStringTest, WireloadTreeToString) {
EXPECT_STREQ(wireloadTreeString(WireloadTree::worst_case), "worst_case_tree");
EXPECT_STREQ(wireloadTreeString(WireloadTree::best_case), "best_case_tree");
EXPECT_STREQ(wireloadTreeString(WireloadTree::balanced), "balanced_tree");
EXPECT_STREQ(wireloadTreeString(WireloadTree::unknown), "unknown");
}
TEST(WireloadStringTest, StringToWireloadTree) {
EXPECT_EQ(stringWireloadTree("worst_case_tree"), WireloadTree::worst_case);
EXPECT_EQ(stringWireloadTree("best_case_tree"), WireloadTree::best_case);
EXPECT_EQ(stringWireloadTree("balanced_tree"), WireloadTree::balanced);
EXPECT_EQ(stringWireloadTree("something_else"), WireloadTree::unknown);
}
TEST(WireloadStringTest, WireloadModeToString) {
EXPECT_STREQ(wireloadModeString(WireloadMode::top), "top");
EXPECT_STREQ(wireloadModeString(WireloadMode::enclosed), "enclosed");
EXPECT_STREQ(wireloadModeString(WireloadMode::segmented), "segmented");
EXPECT_STREQ(wireloadModeString(WireloadMode::unknown), "unknown");
}
TEST(WireloadStringTest, StringToWireloadMode) {
EXPECT_EQ(stringWireloadMode("top"), WireloadMode::top);
EXPECT_EQ(stringWireloadMode("enclosed"), WireloadMode::enclosed);
EXPECT_EQ(stringWireloadMode("segmented"), WireloadMode::segmented);
EXPECT_EQ(stringWireloadMode("something_else"), WireloadMode::unknown);
}
////////////////////////////////////////////////////////////////
// FuncExpr tests - covers constructors, operators, to_string,
// equiv, less, hasPort, copy, deleteSubexprs
////////////////////////////////////////////////////////////////
TEST(FuncExprTest, MakeZero) {
FuncExpr *zero = FuncExpr::makeZero();
EXPECT_NE(zero, nullptr);
EXPECT_EQ(zero->op(), FuncExpr::Op::zero);
EXPECT_EQ(zero->left(), nullptr);
EXPECT_EQ(zero->right(), nullptr);
EXPECT_EQ(zero->port(), nullptr);
EXPECT_EQ(zero->to_string(), "0");
delete zero;
}
TEST(FuncExprTest, MakeOne) {
FuncExpr *one = FuncExpr::makeOne();
EXPECT_NE(one, nullptr);
EXPECT_EQ(one->op(), FuncExpr::Op::one);
EXPECT_EQ(one->to_string(), "1");
delete one;
}
TEST(FuncExprTest, MakeNot) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *not_one = FuncExpr::makeNot(one);
EXPECT_NE(not_one, nullptr);
EXPECT_EQ(not_one->op(), FuncExpr::Op::not_);
EXPECT_EQ(not_one->left(), one);
EXPECT_EQ(not_one->right(), nullptr);
EXPECT_EQ(not_one->to_string(), "!1");
delete not_one;
}
TEST(FuncExprTest, MakeAnd) {
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *and_expr = FuncExpr::makeAnd(zero, one);
EXPECT_NE(and_expr, nullptr);
EXPECT_EQ(and_expr->op(), FuncExpr::Op::and_);
EXPECT_EQ(and_expr->left(), zero);
EXPECT_EQ(and_expr->right(), one);
std::string str = and_expr->to_string();
EXPECT_EQ(str, "0*1");
delete and_expr;
}
TEST(FuncExprTest, MakeOr) {
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *or_expr = FuncExpr::makeOr(zero, one);
EXPECT_NE(or_expr, nullptr);
EXPECT_EQ(or_expr->op(), FuncExpr::Op::or_);
std::string str = or_expr->to_string();
EXPECT_EQ(str, "0+1");
delete or_expr;
}
TEST(FuncExprTest, MakeXor) {
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *xor_expr = FuncExpr::makeXor(zero, one);
EXPECT_NE(xor_expr, nullptr);
EXPECT_EQ(xor_expr->op(), FuncExpr::Op::xor_);
std::string str = xor_expr->to_string();
EXPECT_EQ(str, "0^1");
delete xor_expr;
}
TEST(FuncExprTest, Copy) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *not_one = FuncExpr::makeNot(one);
FuncExpr *copy = not_one->copy();
EXPECT_NE(copy, nullptr);
EXPECT_EQ(copy->op(), FuncExpr::Op::not_);
EXPECT_NE(copy, not_one);
EXPECT_NE(copy->left(), one); // should be deep copy
EXPECT_EQ(copy->left()->op(), FuncExpr::Op::one);
delete not_one;
delete copy;
}
TEST(FuncExprTest, EquivBothNull) {
EXPECT_TRUE(FuncExpr::equiv(nullptr, nullptr));
}
TEST(FuncExprTest, EquivOneNull) {
FuncExpr *one = FuncExpr::makeOne();
EXPECT_FALSE(FuncExpr::equiv(one, nullptr));
EXPECT_FALSE(FuncExpr::equiv(nullptr, one));
delete one;
}
TEST(FuncExprTest, EquivSameOp) {
FuncExpr *one1 = FuncExpr::makeOne();
FuncExpr *one2 = FuncExpr::makeOne();
// Both op_one, same structure - equiv checks sub-expressions
// For op_one, they are equivalent since no sub-expressions exist.
// Actually op_one falls in "default" which checks left/right
EXPECT_TRUE(FuncExpr::equiv(one1, one2));
delete one1;
delete one2;
}
TEST(FuncExprTest, EquivDifferentOp) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *zero = FuncExpr::makeZero();
EXPECT_FALSE(FuncExpr::equiv(one, zero));
delete one;
delete zero;
}
TEST(FuncExprTest, EquivNotExprs) {
FuncExpr *one1 = FuncExpr::makeOne();
FuncExpr *not1 = FuncExpr::makeNot(one1);
FuncExpr *one2 = FuncExpr::makeOne();
FuncExpr *not2 = FuncExpr::makeNot(one2);
EXPECT_TRUE(FuncExpr::equiv(not1, not2));
delete not1;
delete not2;
}
TEST(FuncExprTest, LessBothNull) {
EXPECT_FALSE(FuncExpr::less(nullptr, nullptr));
}
TEST(FuncExprTest, LessOneNull) {
FuncExpr *one = FuncExpr::makeOne();
EXPECT_TRUE(FuncExpr::less(nullptr, one));
EXPECT_FALSE(FuncExpr::less(one, nullptr));
delete one;
}
TEST(FuncExprTest, LessDifferentOps) {
// op_not(1) < op_or is based on enum ordering
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *not_one = FuncExpr::makeNot(one);
FuncExpr *zero1 = FuncExpr::makeZero();
FuncExpr *zero2 = FuncExpr::makeZero();
FuncExpr *or_expr = FuncExpr::makeOr(zero1, zero2);
// op_not=1, op_or=2, so not_one < or_expr
EXPECT_TRUE(FuncExpr::less(not_one, or_expr));
EXPECT_FALSE(FuncExpr::less(or_expr, not_one));
delete not_one;
delete or_expr;
}
TEST(FuncExprTest, HasPortNoPort) {
FuncExpr *one = FuncExpr::makeOne();
EXPECT_FALSE(one->hasPort(nullptr));
delete one;
}
TEST(FuncExprTest, HasPortZero) {
FuncExpr *zero = FuncExpr::makeZero();
EXPECT_FALSE(zero->hasPort(nullptr));
delete zero;
}
TEST(FuncExprTest, HasPortNot) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *not_one = FuncExpr::makeNot(one);
EXPECT_FALSE(not_one->hasPort(nullptr));
delete not_one;
}
TEST(FuncExprTest, HasPortAndOrXor) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *and_expr = FuncExpr::makeAnd(one, zero);
EXPECT_FALSE(and_expr->hasPort(nullptr));
delete and_expr;
}
TEST(FuncExprTest, InvertDoubleNegation) {
// invert() on a NOT expression should unwrap it
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *not_one = FuncExpr::makeNot(one);
FuncExpr *result = not_one->invert();
// Should return 'one' directly and delete the not wrapper
EXPECT_EQ(result->op(), FuncExpr::Op::one);
delete result;
}
TEST(FuncExprTest, InvertNonNot) {
// invert() on non-NOT expression should create NOT wrapper
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *result = one->invert();
EXPECT_EQ(result->op(), FuncExpr::Op::not_);
delete result;
}
TEST(FuncExprTest, PortTimingSenseOne) {
FuncExpr *one = FuncExpr::makeOne();
EXPECT_EQ(one->portTimingSense(nullptr), TimingSense::none);
delete one;
}
TEST(FuncExprTest, PortTimingSenseZero) {
FuncExpr *zero = FuncExpr::makeZero();
EXPECT_EQ(zero->portTimingSense(nullptr), TimingSense::none);
delete zero;
}
TEST(FuncExprTest, PortTimingSenseNotOfOne) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *not_one = FuncExpr::makeNot(one);
// not of constant -> none sense
EXPECT_EQ(not_one->portTimingSense(nullptr), TimingSense::none);
delete not_one;
}
TEST(FuncExprTest, PortTimingSenseAndBothNone) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *and_expr = FuncExpr::makeAnd(one, zero);
// Both have none sense for nullptr port -> returns none
EXPECT_EQ(and_expr->portTimingSense(nullptr), TimingSense::none);
delete and_expr;
}
TEST(FuncExprTest, PortTimingSenseXorNone) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *xor_expr = FuncExpr::makeXor(one, zero);
// XOR with none senses should return unknown
TimingSense sense = xor_expr->portTimingSense(nullptr);
// Both children return none -> falls to else -> unknown
EXPECT_EQ(sense, TimingSense::unknown);
delete xor_expr;
}
TEST(FuncExprTest, CheckSizeOne) {
FuncExpr *one = FuncExpr::makeOne();
EXPECT_FALSE(one->checkSize(1));
EXPECT_FALSE(one->checkSize(4));
delete one;
}
TEST(FuncExprTest, CheckSizeZero) {
FuncExpr *zero = FuncExpr::makeZero();
EXPECT_FALSE(zero->checkSize(1));
delete zero;
}
TEST(FuncExprTest, CheckSizeNot) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *not_one = FuncExpr::makeNot(one);
EXPECT_FALSE(not_one->checkSize(1));
delete not_one;
}
TEST(FuncExprTest, CheckSizeAndOrXor) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *and_expr = FuncExpr::makeAnd(one, zero);
EXPECT_FALSE(and_expr->checkSize(1));
delete and_expr;
}
TEST(FuncExprTest, BitSubExprOne) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *sub = one->bitSubExpr(0);
EXPECT_NE(sub, nullptr);
EXPECT_EQ(sub->op(), FuncExpr::Op::one); // op_one returns a new makeOne()
delete sub;
delete one;
}
TEST(FuncExprTest, BitSubExprZero) {
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *sub = zero->bitSubExpr(0);
EXPECT_NE(sub, nullptr);
EXPECT_EQ(sub->op(), FuncExpr::Op::zero); // op_zero returns a new makeZero()
delete sub;
delete zero;
}
TEST(FuncExprTest, BitSubExprNot) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *not_one = FuncExpr::makeNot(one);
FuncExpr *sub = not_one->bitSubExpr(0);
EXPECT_NE(sub, nullptr);
EXPECT_EQ(sub->op(), FuncExpr::Op::not_);
// Clean up: sub wraps the original one, so delete sub
delete sub;
// not_one's left was consumed by bitSubExpr
delete not_one;
}
TEST(FuncExprTest, BitSubExprOr) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *or_expr = FuncExpr::makeOr(one, zero);
FuncExpr *sub = or_expr->bitSubExpr(0);
EXPECT_NE(sub, nullptr);
EXPECT_EQ(sub->op(), FuncExpr::Op::or_);
delete sub;
delete or_expr;
}
TEST(FuncExprTest, BitSubExprAnd) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *and_expr = FuncExpr::makeAnd(one, zero);
FuncExpr *sub = and_expr->bitSubExpr(0);
EXPECT_NE(sub, nullptr);
EXPECT_EQ(sub->op(), FuncExpr::Op::and_);
delete sub;
delete and_expr;
}
TEST(FuncExprTest, BitSubExprXor) {
FuncExpr *one = FuncExpr::makeOne();
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *xor_expr = FuncExpr::makeXor(one, zero);
FuncExpr *sub = xor_expr->bitSubExpr(0);
EXPECT_NE(sub, nullptr);
EXPECT_EQ(sub->op(), FuncExpr::Op::xor_);
delete sub;
delete xor_expr;
}
TEST(FuncExprTest, LessNotExprs) {
FuncExpr *one1 = FuncExpr::makeOne();
FuncExpr *not1 = FuncExpr::makeNot(one1);
FuncExpr *one2 = FuncExpr::makeOne();
FuncExpr *not2 = FuncExpr::makeNot(one2);
// Same structure -> not less
EXPECT_FALSE(FuncExpr::less(not1, not2));
EXPECT_FALSE(FuncExpr::less(not2, not1));
delete not1;
delete not2;
}
TEST(FuncExprTest, LessDefaultBranch) {
// Test default branch: and/or/xor with equal left
FuncExpr *one1 = FuncExpr::makeOne();
FuncExpr *zero1 = FuncExpr::makeZero();
FuncExpr *and1 = FuncExpr::makeAnd(one1, zero1);
FuncExpr *one2 = FuncExpr::makeOne();
FuncExpr *one3 = FuncExpr::makeOne();
FuncExpr *and2 = FuncExpr::makeAnd(one2, one3);
// and1 left=one, and2 left=one -> equal left, compare right
// and1 right=zero(op_zero=6), and2 right=one(op_one=5), zero > one
EXPECT_FALSE(FuncExpr::less(and1, and2));
EXPECT_TRUE(FuncExpr::less(and2, and1));
delete and1;
delete and2;
}
////////////////////////////////////////////////////////////////
// TableAxis tests - covers axis construction, findAxisIndex,
// findAxisClosestIndex, inBounds, min, max, variableString
////////////////////////////////////////////////////////////////
class TableAxisTest : public ::testing::Test {
protected:
TableAxisPtr makeAxis(TableAxisVariable var,
std::initializer_list<float> vals) {
FloatSeq values;
for (float v : vals)
values.push_back(std::move(v));
return std::make_shared<TableAxis>(var, std::move(values));
}
};
TEST_F(TableAxisTest, BasicProperties) {
auto axis = makeAxis(TableAxisVariable::total_output_net_capacitance,
{1.0f, 2.0f, 3.0f, 4.0f});
EXPECT_EQ(axis->size(), 4u);
EXPECT_EQ(axis->variable(), TableAxisVariable::total_output_net_capacitance);
EXPECT_FLOAT_EQ(axis->axisValue(0), 1.0f);
EXPECT_FLOAT_EQ(axis->axisValue(3), 4.0f);
}
TEST_F(TableAxisTest, MinMax) {
auto axis = makeAxis(TableAxisVariable::input_net_transition,
{0.5f, 1.0f, 2.0f, 5.0f});
EXPECT_FLOAT_EQ(axis->min(), 0.5f);
EXPECT_FLOAT_EQ(axis->max(), 5.0f);
}
TEST_F(TableAxisTest, MinMaxEmpty) {
auto axis = makeAxis(TableAxisVariable::input_net_transition, {});
EXPECT_FLOAT_EQ(axis->min(), 0.0f);
EXPECT_FLOAT_EQ(axis->max(), 0.0f);
}
TEST_F(TableAxisTest, InBounds) {
auto axis = makeAxis(TableAxisVariable::input_net_transition,
{1.0f, 2.0f, 3.0f});
EXPECT_TRUE(axis->inBounds(1.5f));
EXPECT_TRUE(axis->inBounds(1.0f));
EXPECT_TRUE(axis->inBounds(3.0f));
EXPECT_FALSE(axis->inBounds(0.5f));
EXPECT_FALSE(axis->inBounds(3.5f));
}
TEST_F(TableAxisTest, InBoundsSingleElement) {
auto axis = makeAxis(TableAxisVariable::input_net_transition, {1.0f});
// Single element -> size <= 1 -> false
EXPECT_FALSE(axis->inBounds(1.0f));
}
TEST_F(TableAxisTest, FindAxisIndex) {
auto axis = makeAxis(TableAxisVariable::total_output_net_capacitance,
{1.0f, 2.0f, 4.0f, 8.0f});
// value below min -> 0
EXPECT_EQ(axis->findAxisIndex(0.5f), 0u);
// value at min -> 0
EXPECT_EQ(axis->findAxisIndex(1.0f), 0u);
// value between 1.0 and 2.0 -> 0
EXPECT_EQ(axis->findAxisIndex(1.5f), 0u);
// value at second point -> 1
EXPECT_EQ(axis->findAxisIndex(2.0f), 1u);
// value between 2.0 and 4.0 -> 1
EXPECT_EQ(axis->findAxisIndex(3.0f), 1u);
// value between 4.0 and 8.0 -> 2
EXPECT_EQ(axis->findAxisIndex(6.0f), 2u);
// value above max -> size-2 = 2
EXPECT_EQ(axis->findAxisIndex(10.0f), 2u);
}
TEST_F(TableAxisTest, FindAxisIndexSingleElement) {
auto axis = makeAxis(TableAxisVariable::total_output_net_capacitance,
{5.0f});
// Single element -> returns 0
EXPECT_EQ(axis->findAxisIndex(5.0f), 0u);
EXPECT_EQ(axis->findAxisIndex(1.0f), 0u);
EXPECT_EQ(axis->findAxisIndex(10.0f), 0u);
}
TEST_F(TableAxisTest, FindAxisClosestIndex) {
auto axis = makeAxis(TableAxisVariable::total_output_net_capacitance,
{1.0f, 3.0f, 5.0f, 7.0f});
// Below min -> 0
EXPECT_EQ(axis->findAxisClosestIndex(0.0f), 0u);
// Above max -> size-1
EXPECT_EQ(axis->findAxisClosestIndex(10.0f), 3u);
// Close to 1.0 -> 0
EXPECT_EQ(axis->findAxisClosestIndex(1.5f), 0u);
// Close to 3.0 -> 1
EXPECT_EQ(axis->findAxisClosestIndex(2.8f), 1u);
// Midpoint: 4.0 between 3.0 and 5.0 -> closer to upper (5.0)
EXPECT_EQ(axis->findAxisClosestIndex(4.0f), 2u);
// Exact match
EXPECT_EQ(axis->findAxisClosestIndex(5.0f), 2u);
}
TEST_F(TableAxisTest, FindAxisIndexExact) {
auto axis = makeAxis(TableAxisVariable::total_output_net_capacitance,
{1.0f, 2.0f, 4.0f, 8.0f});
size_t index;
bool exists;
axis->findAxisIndex(2.0f, index, exists);
EXPECT_TRUE(exists);
EXPECT_EQ(index, 1u);
axis->findAxisIndex(4.0f, index, exists);
EXPECT_TRUE(exists);
EXPECT_EQ(index, 2u);
axis->findAxisIndex(3.0f, index, exists);
EXPECT_FALSE(exists);
// Out of range
axis->findAxisIndex(0.5f, index, exists);
EXPECT_FALSE(exists);
axis->findAxisIndex(10.0f, index, exists);
EXPECT_FALSE(exists);
}
TEST_F(TableAxisTest, VariableString) {
auto axis = makeAxis(TableAxisVariable::total_output_net_capacitance,
{1.0f});
const char *str = axis->variableString();
EXPECT_NE(str, nullptr);
EXPECT_STREQ(str, "total_output_net_capacitance");
}
TEST_F(TableAxisTest, UnitLookup) {
Units units;
auto axis = makeAxis(TableAxisVariable::total_output_net_capacitance,
{1.0f});
const Unit *unit = axis->unit(&units);
EXPECT_NE(unit, nullptr);
}
////////////////////////////////////////////////////////////////
// Table variable string conversion tests
////////////////////////////////////////////////////////////////
TEST(TableVariableTest, 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("input_transition_time"),
TableAxisVariable::input_transition_time);
EXPECT_EQ(stringTableAxisVariable("related_pin_transition"),
TableAxisVariable::related_pin_transition);
EXPECT_EQ(stringTableAxisVariable("constrained_pin_transition"),
TableAxisVariable::constrained_pin_transition);
EXPECT_EQ(stringTableAxisVariable("output_pin_transition"),
TableAxisVariable::output_pin_transition);
EXPECT_EQ(stringTableAxisVariable("connect_delay"),
TableAxisVariable::connect_delay);
EXPECT_EQ(stringTableAxisVariable("related_out_total_output_net_capacitance"),
TableAxisVariable::related_out_total_output_net_capacitance);
EXPECT_EQ(stringTableAxisVariable("time"),
TableAxisVariable::time);
EXPECT_EQ(stringTableAxisVariable("iv_output_voltage"),
TableAxisVariable::iv_output_voltage);
EXPECT_EQ(stringTableAxisVariable("input_noise_width"),
TableAxisVariable::input_noise_width);
EXPECT_EQ(stringTableAxisVariable("input_noise_height"),
TableAxisVariable::input_noise_height);
EXPECT_EQ(stringTableAxisVariable("input_voltage"),
TableAxisVariable::input_voltage);
EXPECT_EQ(stringTableAxisVariable("output_voltage"),
TableAxisVariable::output_voltage);
EXPECT_EQ(stringTableAxisVariable("path_depth"),
TableAxisVariable::path_depth);
EXPECT_EQ(stringTableAxisVariable("path_distance"),
TableAxisVariable::path_distance);
EXPECT_EQ(stringTableAxisVariable("normalized_voltage"),
TableAxisVariable::normalized_voltage);
EXPECT_EQ(stringTableAxisVariable("nonexistent"),
TableAxisVariable::unknown);
}
TEST(TableVariableTest, TableVariableString) {
EXPECT_STREQ(tableVariableString(TableAxisVariable::total_output_net_capacitance),
"total_output_net_capacitance");
EXPECT_STREQ(tableVariableString(TableAxisVariable::input_net_transition),
"input_net_transition");
EXPECT_STREQ(tableVariableString(TableAxisVariable::time),
"time");
}
TEST(TableVariableTest, TableVariableUnit) {
Units units;
// Capacitance variables
const Unit *u = tableVariableUnit(TableAxisVariable::total_output_net_capacitance,
&units);
EXPECT_EQ(u, units.capacitanceUnit());
u = tableVariableUnit(TableAxisVariable::related_out_total_output_net_capacitance,
&units);
EXPECT_EQ(u, units.capacitanceUnit());
u = tableVariableUnit(TableAxisVariable::equal_or_opposite_output_net_capacitance,
&units);
EXPECT_EQ(u, units.capacitanceUnit());
// Time variables
u = tableVariableUnit(TableAxisVariable::input_net_transition, &units);
EXPECT_EQ(u, units.timeUnit());
u = tableVariableUnit(TableAxisVariable::input_transition_time, &units);
EXPECT_EQ(u, units.timeUnit());
u = tableVariableUnit(TableAxisVariable::related_pin_transition, &units);
EXPECT_EQ(u, units.timeUnit());
u = tableVariableUnit(TableAxisVariable::constrained_pin_transition, &units);
EXPECT_EQ(u, units.timeUnit());
u = tableVariableUnit(TableAxisVariable::output_pin_transition, &units);
EXPECT_EQ(u, units.timeUnit());
u = tableVariableUnit(TableAxisVariable::connect_delay, &units);
EXPECT_EQ(u, units.timeUnit());
u = tableVariableUnit(TableAxisVariable::time, &units);
EXPECT_EQ(u, units.timeUnit());
u = tableVariableUnit(TableAxisVariable::input_noise_height, &units);
EXPECT_EQ(u, units.timeUnit());
// Voltage variables
u = tableVariableUnit(TableAxisVariable::input_voltage, &units);
EXPECT_EQ(u, units.voltageUnit());
u = tableVariableUnit(TableAxisVariable::output_voltage, &units);
EXPECT_EQ(u, units.voltageUnit());
u = tableVariableUnit(TableAxisVariable::iv_output_voltage, &units);
EXPECT_EQ(u, units.voltageUnit());
u = tableVariableUnit(TableAxisVariable::input_noise_width, &units);
EXPECT_EQ(u, units.voltageUnit());
// Distance
u = tableVariableUnit(TableAxisVariable::path_distance, &units);
EXPECT_EQ(u, units.distanceUnit());
// Scalar
u = tableVariableUnit(TableAxisVariable::path_depth, &units);
EXPECT_EQ(u, units.scalarUnit());
u = tableVariableUnit(TableAxisVariable::normalized_voltage, &units);
EXPECT_EQ(u, units.scalarUnit());
u = tableVariableUnit(TableAxisVariable::unknown, &units);
EXPECT_EQ(u, units.scalarUnit());
}
////////////////////////////////////////////////////////////////
// Table0 tests (scalar table)
////////////////////////////////////////////////////////////////
TEST(Table0Test, BasicValue) {
Table table(42.0f);
EXPECT_EQ(table.order(), 0);
EXPECT_FLOAT_EQ(table.value(0, 0, 0), 42.0f);
EXPECT_FLOAT_EQ(table.findValue(0.0f, 0.0f, 0.0f), 42.0f);
EXPECT_FLOAT_EQ(table.findValue(1.0f, 2.0f, 3.0f), 42.0f);
EXPECT_EQ(table.axis1(), nullptr);
EXPECT_EQ(table.axis2(), nullptr);
EXPECT_EQ(table.axis3(), nullptr);
}
////////////////////////////////////////////////////////////////
// Table1 tests (1D table)
////////////////////////////////////////////////////////////////
class Table1Test : public ::testing::Test {
protected:
TableAxisPtr makeAxis(std::initializer_list<float> vals) {
FloatSeq values;
for (float v : vals)
values.push_back(std::move(v));
return std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(values));
}
};
TEST_F(Table1Test, DefaultConstructor) {
Table table;
// Unified Table default constructor creates order 0
EXPECT_EQ(table.order(), 0);
}
TEST_F(Table1Test, ValueLookup) {
auto axis = makeAxis({1.0f, 2.0f, 4.0f});
FloatSeq vals;
vals.push_back(10.0f);
vals.push_back(20.0f);
vals.push_back(40.0f);
Table table(std::move(vals), axis);
EXPECT_EQ(table.order(), 1);
EXPECT_FLOAT_EQ(table.value(0), 10.0f);
EXPECT_FLOAT_EQ(table.value(1), 20.0f);
EXPECT_FLOAT_EQ(table.value(2), 40.0f);
EXPECT_NE(table.axis1(), nullptr);
}
TEST_F(Table1Test, FindValueInterpolation) {
auto axis = makeAxis({0.0f, 1.0f});
FloatSeq vals;
vals.push_back(0.0f);
vals.push_back(10.0f);
Table table(std::move(vals), axis);
// Exact match at lower bound
EXPECT_FLOAT_EQ(table.findValue(0.0f), 0.0f);
// Midpoint
EXPECT_NEAR(table.findValue(0.5f), 5.0f, 0.01f);
// Extrapolation beyond upper bound
float val = table.findValue(2.0f);
// Linear extrapolation: 20.0
EXPECT_NEAR(val, 20.0f, 0.01f);
}
TEST_F(Table1Test, FindValueClip) {
auto axis = makeAxis({1.0f, 3.0f});
FloatSeq vals;
vals.push_back(10.0f);
vals.push_back(30.0f);
Table table(std::move(vals), axis);
// Below range -> clip to 0
EXPECT_FLOAT_EQ(table.findValueClip(0.0f), 0.0f);
// In range
EXPECT_NEAR(table.findValueClip(2.0f), 20.0f, 0.01f);
// Above range -> clip to last value
EXPECT_FLOAT_EQ(table.findValueClip(4.0f), 30.0f);
}
TEST_F(Table1Test, FindValueSingleElement) {
auto axis = makeAxis({5.0f});
FloatSeq vals;
vals.push_back(42.0f);
Table table(std::move(vals), axis);
// Single element: findValue(float) -> value(size_t(float))
// Only index 0 is valid, so pass 0.0f which converts to index 0.
EXPECT_FLOAT_EQ(table.findValue(0.0f), 42.0f);
// Also test findValueClip for single element
EXPECT_FLOAT_EQ(table.findValueClip(0.0f), 42.0f);
}
TEST_F(Table1Test, CopyConstructor) {
auto axis = makeAxis({1.0f, 2.0f});
FloatSeq vals;
vals.push_back(10.0f);
vals.push_back(20.0f);
Table table(std::move(vals), axis);
Table copy(table);
EXPECT_FLOAT_EQ(copy.value(0), 10.0f);
EXPECT_FLOAT_EQ(copy.value(1), 20.0f);
}
TEST_F(Table1Test, MoveConstructor) {
auto axis = makeAxis({1.0f, 2.0f});
FloatSeq vals;
vals.push_back(10.0f);
vals.push_back(20.0f);
Table table(std::move(vals), axis);
Table moved(std::move(table));
EXPECT_FLOAT_EQ(moved.value(0), 10.0f);
EXPECT_FLOAT_EQ(moved.value(1), 20.0f);
}
TEST_F(Table1Test, MoveAssignment) {
auto axis1 = makeAxis({1.0f, 2.0f});
FloatSeq vals1;
vals1.push_back(10.0f);
vals1.push_back(20.0f);
Table table1(std::move(vals1), axis1);
auto axis2 = makeAxis({3.0f, 4.0f});
FloatSeq vals2;
vals2.push_back(30.0f);
vals2.push_back(40.0f);
Table table2(std::move(vals2), axis2);
table2 = std::move(table1);
EXPECT_FLOAT_EQ(table2.value(0), 10.0f);
EXPECT_FLOAT_EQ(table2.value(1), 20.0f);
}
TEST_F(Table1Test, ValueViaThreeArgs) {
auto axis = makeAxis({1.0f, 3.0f});
FloatSeq vals;
vals.push_back(10.0f);
vals.push_back(30.0f);
Table table(std::move(vals), axis);
// The three-arg findValue just uses the first arg
EXPECT_NEAR(table.findValue(2.0f, 0.0f, 0.0f), 20.0f, 0.01f);
EXPECT_NEAR(table.findValue(1.0f, 0.0f, 0.0f), 10.0f, 0.01f);
// value(idx, idx, idx) also just uses first
EXPECT_FLOAT_EQ(table.value(0, 0, 0), 10.0f);
EXPECT_FLOAT_EQ(table.value(1, 0, 0), 30.0f);
}
////////////////////////////////////////////////////////////////
// Table2 tests (2D table)
////////////////////////////////////////////////////////////////
TEST(Table2Test, BilinearInterpolation) {
FloatSeq axis1_vals;
axis1_vals.push_back(0.0f);
axis1_vals.push_back(2.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_net_transition, std::move(axis1_vals));
FloatSeq axis2_vals;
axis2_vals.push_back(0.0f);
axis2_vals.push_back(4.0f);
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(axis2_vals));
FloatTable values;
FloatSeq row0;
row0.push_back(0.0f);
row0.push_back(4.0f);
values.push_back(std::move(row0));
FloatSeq row1;
row1.push_back(2.0f);
row1.push_back(6.0f);
values.push_back(std::move(row1));
Table table(std::move(values), axis1, axis2);
EXPECT_EQ(table.order(), 2);
// Corner values
EXPECT_FLOAT_EQ(table.value(0, 0), 0.0f);
EXPECT_FLOAT_EQ(table.value(0, 1), 4.0f);
EXPECT_FLOAT_EQ(table.value(1, 0), 2.0f);
EXPECT_FLOAT_EQ(table.value(1, 1), 6.0f);
// Center (bilinear interpolation)
EXPECT_NEAR(table.findValue(1.0f, 2.0f, 0.0f), 3.0f, 0.01f);
}
TEST(Table2Test, SingleRowInterpolation) {
FloatSeq axis1_vals;
axis1_vals.push_back(0.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_net_transition, std::move(axis1_vals));
FloatSeq axis2_vals;
axis2_vals.push_back(0.0f);
axis2_vals.push_back(4.0f);
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(axis2_vals));
FloatTable values;
FloatSeq row0;
row0.push_back(10.0f);
row0.push_back(30.0f);
values.push_back(std::move(row0));
Table table(std::move(values), axis1, axis2);
// Size1==1, so use axis2 only interpolation
EXPECT_NEAR(table.findValue(0.0f, 2.0f, 0.0f), 20.0f, 0.01f);
}
TEST(Table2Test, SingleColumnInterpolation) {
FloatSeq axis1_vals;
axis1_vals.push_back(0.0f);
axis1_vals.push_back(4.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_net_transition, std::move(axis1_vals));
FloatSeq axis2_vals;
axis2_vals.push_back(0.0f);
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(axis2_vals));
FloatTable values;
FloatSeq row0;
row0.push_back(10.0f);
values.push_back(std::move(row0));
FloatSeq row1;
row1.push_back(30.0f);
values.push_back(std::move(row1));
Table table(std::move(values), axis1, axis2);
// Size2==1, so use axis1 only interpolation
EXPECT_NEAR(table.findValue(2.0f, 0.0f, 0.0f), 20.0f, 0.01f);
}
TEST(Table2Test, SingleCellValue) {
FloatSeq axis1_vals;
axis1_vals.push_back(0.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_net_transition, std::move(axis1_vals));
FloatSeq axis2_vals;
axis2_vals.push_back(0.0f);
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(axis2_vals));
FloatTable values;
FloatSeq row0;
row0.push_back(42.0f);
values.push_back(std::move(row0));
Table table(std::move(values), axis1, axis2);
EXPECT_FLOAT_EQ(table.findValue(0.0f, 0.0f, 0.0f), 42.0f);
}
////////////////////////////////////////////////////////////////
// TimingType/TimingSense string conversions
////////////////////////////////////////////////////////////////
TEST(TimingTypeTest, FindTimingType) {
EXPECT_EQ(findTimingType("combinational"), TimingType::combinational);
EXPECT_EQ(findTimingType("setup_rising"), TimingType::setup_rising);
EXPECT_EQ(findTimingType("setup_falling"), TimingType::setup_falling);
EXPECT_EQ(findTimingType("hold_rising"), TimingType::hold_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("clear"), TimingType::clear);
EXPECT_EQ(findTimingType("preset"), TimingType::preset);
EXPECT_EQ(findTimingType("three_state_enable"), TimingType::three_state_enable);
EXPECT_EQ(findTimingType("three_state_disable"), TimingType::three_state_disable);
EXPECT_EQ(findTimingType("recovery_rising"), TimingType::recovery_rising);
EXPECT_EQ(findTimingType("removal_falling"), TimingType::removal_falling);
EXPECT_EQ(findTimingType("min_pulse_width"), TimingType::min_pulse_width);
EXPECT_EQ(findTimingType("minimum_period"), TimingType::minimum_period);
EXPECT_EQ(findTimingType("nonexistent"), TimingType::unknown);
}
TEST(TimingTypeTest, TimingTypeIsCheck) {
EXPECT_TRUE(timingTypeIsCheck(TimingType::setup_rising));
EXPECT_TRUE(timingTypeIsCheck(TimingType::setup_falling));
EXPECT_TRUE(timingTypeIsCheck(TimingType::hold_rising));
EXPECT_TRUE(timingTypeIsCheck(TimingType::hold_falling));
EXPECT_TRUE(timingTypeIsCheck(TimingType::recovery_rising));
EXPECT_TRUE(timingTypeIsCheck(TimingType::recovery_falling));
EXPECT_TRUE(timingTypeIsCheck(TimingType::removal_rising));
EXPECT_TRUE(timingTypeIsCheck(TimingType::removal_falling));
EXPECT_TRUE(timingTypeIsCheck(TimingType::min_pulse_width));
EXPECT_TRUE(timingTypeIsCheck(TimingType::minimum_period));
EXPECT_TRUE(timingTypeIsCheck(TimingType::skew_rising));
EXPECT_TRUE(timingTypeIsCheck(TimingType::skew_falling));
EXPECT_TRUE(timingTypeIsCheck(TimingType::nochange_high_high));
EXPECT_TRUE(timingTypeIsCheck(TimingType::nochange_high_low));
EXPECT_TRUE(timingTypeIsCheck(TimingType::nochange_low_high));
EXPECT_TRUE(timingTypeIsCheck(TimingType::nochange_low_low));
EXPECT_TRUE(timingTypeIsCheck(TimingType::non_seq_setup_falling));
EXPECT_TRUE(timingTypeIsCheck(TimingType::non_seq_setup_rising));
EXPECT_TRUE(timingTypeIsCheck(TimingType::non_seq_hold_falling));
EXPECT_TRUE(timingTypeIsCheck(TimingType::non_seq_hold_rising));
EXPECT_TRUE(timingTypeIsCheck(TimingType::retaining_time));
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));
}
TEST(TimingTypeTest, TimingTypeScaleFactorType) {
EXPECT_EQ(timingTypeScaleFactorType(TimingType::setup_rising),
ScaleFactorType::setup);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::setup_falling),
ScaleFactorType::setup);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::hold_rising),
ScaleFactorType::hold);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::hold_falling),
ScaleFactorType::hold);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::recovery_rising),
ScaleFactorType::recovery);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::removal_falling),
ScaleFactorType::removal);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::skew_rising),
ScaleFactorType::skew);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::minimum_period),
ScaleFactorType::min_period);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::nochange_high_high),
ScaleFactorType::nochange);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::min_pulse_width),
ScaleFactorType::min_pulse_width);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::combinational),
ScaleFactorType::cell);
}
TEST(TimingSenseTest, ToString) {
EXPECT_STREQ(to_string(TimingSense::positive_unate), "positive_unate");
EXPECT_STREQ(to_string(TimingSense::negative_unate), "negative_unate");
EXPECT_STREQ(to_string(TimingSense::non_unate), "non_unate");
EXPECT_STREQ(to_string(TimingSense::none), "none");
EXPECT_STREQ(to_string(TimingSense::unknown), "unknown");
}
TEST(TimingSenseTest, Opposite) {
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::unknown),
TimingSense::unknown);
EXPECT_EQ(timingSenseOpposite(TimingSense::none),
TimingSense::none);
}
////////////////////////////////////////////////////////////////
// RiseFallValues tests
////////////////////////////////////////////////////////////////
TEST(RiseFallValuesTest, DefaultConstructor) {
RiseFallValues rfv;
EXPECT_FALSE(rfv.hasValue(RiseFall::rise()));
EXPECT_FALSE(rfv.hasValue(RiseFall::fall()));
}
TEST(RiseFallValuesTest, InitValueConstructor) {
RiseFallValues rfv(3.14f);
EXPECT_TRUE(rfv.hasValue(RiseFall::rise()));
EXPECT_TRUE(rfv.hasValue(RiseFall::fall()));
EXPECT_FLOAT_EQ(rfv.value(RiseFall::rise()), 3.14f);
EXPECT_FLOAT_EQ(rfv.value(RiseFall::fall()), 3.14f);
}
TEST(RiseFallValuesTest, SetValueRiseFall) {
RiseFallValues rfv;
rfv.setValue(RiseFall::rise(), 1.0f);
EXPECT_TRUE(rfv.hasValue(RiseFall::rise()));
EXPECT_FALSE(rfv.hasValue(RiseFall::fall()));
EXPECT_FLOAT_EQ(rfv.value(RiseFall::rise()), 1.0f);
}
TEST(RiseFallValuesTest, SetValueBoth) {
RiseFallValues rfv;
rfv.setValue(2.5f);
EXPECT_TRUE(rfv.hasValue(RiseFall::rise()));
EXPECT_TRUE(rfv.hasValue(RiseFall::fall()));
EXPECT_FLOAT_EQ(rfv.value(RiseFall::rise()), 2.5f);
EXPECT_FLOAT_EQ(rfv.value(RiseFall::fall()), 2.5f);
}
TEST(RiseFallValuesTest, SetValueRiseFallBoth) {
RiseFallValues rfv;
rfv.setValue(RiseFallBoth::riseFall(), 5.0f);
EXPECT_TRUE(rfv.hasValue(RiseFall::rise()));
EXPECT_TRUE(rfv.hasValue(RiseFall::fall()));
EXPECT_FLOAT_EQ(rfv.value(RiseFall::rise()), 5.0f);
EXPECT_FLOAT_EQ(rfv.value(RiseFall::fall()), 5.0f);
}
TEST(RiseFallValuesTest, SetValueRiseOnly) {
RiseFallValues rfv;
rfv.setValue(RiseFallBoth::rise(), 1.0f);
EXPECT_TRUE(rfv.hasValue(RiseFall::rise()));
EXPECT_FALSE(rfv.hasValue(RiseFall::fall()));
EXPECT_FLOAT_EQ(rfv.value(RiseFall::rise()), 1.0f);
}
TEST(RiseFallValuesTest, ValueWithExists) {
RiseFallValues rfv;
float val;
bool exists;
rfv.value(RiseFall::rise(), val, exists);
EXPECT_FALSE(exists);
rfv.setValue(RiseFall::rise(), 7.0f);
rfv.value(RiseFall::rise(), val, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(val, 7.0f);
}
TEST(RiseFallValuesTest, SetValues) {
RiseFallValues src(10.0f);
RiseFallValues dst;
dst.setValues(&src);
EXPECT_TRUE(dst.hasValue(RiseFall::rise()));
EXPECT_TRUE(dst.hasValue(RiseFall::fall()));
EXPECT_FLOAT_EQ(dst.value(RiseFall::rise()), 10.0f);
EXPECT_FLOAT_EQ(dst.value(RiseFall::fall()), 10.0f);
}
TEST(RiseFallValuesTest, Clear) {
RiseFallValues rfv(5.0f);
rfv.clear();
EXPECT_FALSE(rfv.hasValue(RiseFall::rise()));
EXPECT_FALSE(rfv.hasValue(RiseFall::fall()));
}
////////////////////////////////////////////////////////////////
// InternalPower tests (InternalPowerAttrs removed in MCMM update)
////////////////////////////////////////////////////////////////
TEST(InternalPowerTest, DirectConstruction) {
// InternalPower is now constructed directly
InternalPowerModels models{};
std::shared_ptr<FuncExpr> when_expr(FuncExpr::makeOne());
InternalPower pwr(nullptr, nullptr, nullptr, when_expr, models);
EXPECT_EQ(pwr.when(), when_expr.get());
EXPECT_EQ(pwr.relatedPgPin(), nullptr);
EXPECT_EQ(pwr.model(RiseFall::rise()), nullptr);
EXPECT_EQ(pwr.model(RiseFall::fall()), nullptr);
}
////////////////////////////////////////////////////////////////
// TimingArcAttrs tests
////////////////////////////////////////////////////////////////
TEST(TimingArcAttrsTest, DefaultConstructor) {
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());
EXPECT_FLOAT_EQ(attrs.ocvArcDepth(), 0.0f);
EXPECT_EQ(attrs.model(RiseFall::rise()), nullptr);
EXPECT_EQ(attrs.model(RiseFall::fall()), nullptr);
}
TEST(TimingArcAttrsTest, SenseConstructor) {
TimingArcAttrs attrs(TimingSense::positive_unate);
EXPECT_EQ(attrs.timingSense(), TimingSense::positive_unate);
EXPECT_EQ(attrs.timingType(), TimingType::combinational);
}
TEST(TimingArcAttrsTest, SetTimingType) {
TimingArcAttrs attrs;
attrs.setTimingType(TimingType::setup_rising);
EXPECT_EQ(attrs.timingType(), TimingType::setup_rising);
}
TEST(TimingArcAttrsTest, SetTimingSense) {
TimingArcAttrs attrs;
attrs.setTimingSense(TimingSense::negative_unate);
EXPECT_EQ(attrs.timingSense(), TimingSense::negative_unate);
}
TEST(TimingArcAttrsTest, SetOcvArcDepth) {
TimingArcAttrs attrs;
attrs.setOcvArcDepth(2.5f);
EXPECT_FLOAT_EQ(attrs.ocvArcDepth(), 2.5f);
}
TEST(TimingArcAttrsTest, SetModeName) {
TimingArcAttrs attrs;
attrs.setModeName("test_mode");
EXPECT_EQ(attrs.modeName(), "test_mode");
attrs.setModeName("another_mode");
EXPECT_EQ(attrs.modeName(), "another_mode");
}
TEST(TimingArcAttrsTest, SetModeValue) {
TimingArcAttrs attrs;
attrs.setModeValue("mode_val");
EXPECT_EQ(attrs.modeValue(), "mode_val");
}
TEST(TimingArcAttrsTest, SetSdfCond) {
TimingArcAttrs attrs;
attrs.setSdfCond("A==1");
EXPECT_EQ(attrs.sdfCond(), "A==1");
// After setSdfCond, sdfCondStart and sdfCondEnd point to same string
EXPECT_EQ(attrs.sdfCondStart(), "A==1");
EXPECT_EQ(attrs.sdfCondEnd(), "A==1");
}
TEST(TimingArcAttrsTest, SetSdfCondStartEnd) {
TimingArcAttrs attrs;
attrs.setSdfCondStart("start_cond");
EXPECT_EQ(attrs.sdfCondStart(), "start_cond");
attrs.setSdfCondEnd("end_cond");
EXPECT_EQ(attrs.sdfCondEnd(), "end_cond");
}
////////////////////////////////////////////////////////////////
// Transition/RiseFall tests
////////////////////////////////////////////////////////////////
TEST(RiseFallTest, BasicProperties) {
EXPECT_EQ(RiseFall::rise()->index(), 0);
EXPECT_EQ(RiseFall::fall()->index(), 1);
EXPECT_STREQ(RiseFall::rise()->name(), "rise");
EXPECT_STREQ(RiseFall::fall()->name(), "fall");
EXPECT_EQ(RiseFall::rise()->opposite(), RiseFall::fall());
EXPECT_EQ(RiseFall::fall()->opposite(), RiseFall::rise());
}
TEST(RiseFallTest, Find) {
EXPECT_EQ(RiseFall::find("rise"), RiseFall::rise());
EXPECT_EQ(RiseFall::find("fall"), RiseFall::fall());
EXPECT_EQ(RiseFall::find(0), RiseFall::rise());
EXPECT_EQ(RiseFall::find(1), RiseFall::fall());
}
TEST(RiseFallTest, Range) {
auto &range = RiseFall::range();
EXPECT_EQ(range.size(), 2u);
EXPECT_EQ(range[0], RiseFall::rise());
EXPECT_EQ(range[1], RiseFall::fall());
}
TEST(TransitionTest, BasicProperties) {
EXPECT_EQ(Transition::rise()->asRiseFall(), RiseFall::rise());
EXPECT_EQ(Transition::fall()->asRiseFall(), RiseFall::fall());
}
TEST(TransitionTest, Find) {
// Transition names are "^" and "v", not "rise" and "fall"
EXPECT_EQ(Transition::find("^"), Transition::rise());
EXPECT_EQ(Transition::find("v"), Transition::fall());
// Also findable by init_final strings
EXPECT_EQ(Transition::find("01"), Transition::rise());
EXPECT_EQ(Transition::find("10"), Transition::fall());
}
TEST(RiseFallBothTest, Matches) {
EXPECT_TRUE(RiseFallBoth::riseFall()->matches(RiseFall::rise()));
EXPECT_TRUE(RiseFallBoth::riseFall()->matches(RiseFall::fall()));
EXPECT_TRUE(RiseFallBoth::rise()->matches(RiseFall::rise()));
EXPECT_FALSE(RiseFallBoth::rise()->matches(RiseFall::fall()));
EXPECT_FALSE(RiseFallBoth::fall()->matches(RiseFall::rise()));
EXPECT_TRUE(RiseFallBoth::fall()->matches(RiseFall::fall()));
}
////////////////////////////////////////////////////////////////
// WireloadSelection tests
////////////////////////////////////////////////////////////////
TEST(WireloadSelectionTest, FindWireloadBasic) {
// Create a mock library to use with Wireload
LibertyLibrary lib("test_lib", "test.lib");
Wireload wl_small("small", &lib, 0.0f, 1.0f, 1.0f, 0.0f);
Wireload wl_medium("medium", &lib, 0.0f, 2.0f, 2.0f, 0.0f);
Wireload wl_large("large", &lib, 0.0f, 3.0f, 3.0f, 0.0f);
WireloadSelection sel("test_sel");
sel.addWireloadFromArea(0.0f, 100.0f, &wl_small);
sel.addWireloadFromArea(100.0f, 500.0f, &wl_medium);
sel.addWireloadFromArea(500.0f, 1000.0f, &wl_large);
// Below minimum -> first
EXPECT_EQ(sel.findWireload(-1.0f), &wl_small);
// At minimum
EXPECT_EQ(sel.findWireload(0.0f), &wl_small);
// In second range
EXPECT_EQ(sel.findWireload(200.0f), &wl_medium);
// At max
EXPECT_EQ(sel.findWireload(500.0f), &wl_large);
// Above max
EXPECT_EQ(sel.findWireload(2000.0f), &wl_large);
}
////////////////////////////////////////////////////////////////
// LinearModel tests - covers GateLinearModel and CheckLinearModel
////////////////////////////////////////////////////////////////
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_;
};
TEST_F(LinearModelTest, GateLinearModelConstruct) {
GateLinearModel model(cell_, 1.5f, 0.5f);
EXPECT_FLOAT_EQ(model.driveResistance(nullptr), 0.5f);
}
TEST_F(LinearModelTest, GateLinearModelGateDelay) {
GateLinearModel model(cell_, 1.0f, 2.0f);
ArcDelay gate_delay;
Slew drvr_slew;
// delay = intrinsic + resistance * load_cap = 1.0 + 2.0 * 3.0 = 7.0
model.gateDelay(nullptr, 0.0f, 3.0f, false, gate_delay, drvr_slew);
EXPECT_FLOAT_EQ(delayAsFloat(gate_delay), 7.0f);
EXPECT_FLOAT_EQ(delayAsFloat(drvr_slew), 0.0f);
}
TEST_F(LinearModelTest, GateLinearModelZeroLoad) {
GateLinearModel model(cell_, 2.5f, 1.0f);
ArcDelay gate_delay;
Slew drvr_slew;
// delay = 2.5 + 1.0 * 0.0 = 2.5
model.gateDelay(nullptr, 0.0f, 0.0f, false, gate_delay, drvr_slew);
EXPECT_FLOAT_EQ(delayAsFloat(gate_delay), 2.5f);
}
TEST_F(LinearModelTest, GateLinearModelReportGateDelay) {
GateLinearModel model(cell_, 1.0f, 2.0f);
std::string report = model.reportGateDelay(nullptr, 0.0f, 0.5f, false, 3);
EXPECT_FALSE(report.empty());
// Report should contain "Delay ="
EXPECT_NE(report.find("Delay"), std::string::npos);
}
TEST_F(LinearModelTest, CheckLinearModelConstruct) {
CheckLinearModel model(cell_, 3.0f);
ArcDelay delay = model.checkDelay(nullptr, 0.0f, 0.0f, 0.0f, false);
EXPECT_FLOAT_EQ(delayAsFloat(delay), 3.0f);
}
TEST_F(LinearModelTest, CheckLinearModelCheckDelay) {
CheckLinearModel model(cell_, 5.5f);
// checkDelay always returns intrinsic_ regardless of other params
ArcDelay delay1 = model.checkDelay(nullptr, 1.0f, 2.0f, 3.0f, true);
EXPECT_FLOAT_EQ(delayAsFloat(delay1), 5.5f);
ArcDelay delay2 = model.checkDelay(nullptr, 0.0f, 0.0f, 0.0f, false);
EXPECT_FLOAT_EQ(delayAsFloat(delay2), 5.5f);
}
TEST_F(LinearModelTest, CheckLinearModelReportCheckDelay) {
CheckLinearModel model(cell_, 2.0f);
std::string report = model.reportCheckDelay(nullptr, 0.0f, nullptr,
0.0f, 0.0f, false, 3);
EXPECT_FALSE(report.empty());
EXPECT_NE(report.find("Check"), std::string::npos);
}
////////////////////////////////////////////////////////////////
// InternalPower additional coverage
////////////////////////////////////////////////////////////////
TEST(InternalPowerTest, ModelAccess) {
InternalPowerModels models{};
InternalPower pwr(nullptr, nullptr, nullptr, nullptr, models);
// Initially models should be nullptr
EXPECT_EQ(pwr.model(RiseFall::rise()), nullptr);
EXPECT_EQ(pwr.model(RiseFall::fall()), nullptr);
}
TEST(InternalPowerTest, WithModel) {
// Create a minimal model: Table -> TableModel -> InternalPowerModel
TablePtr tbl = std::make_shared<Table>(1.0f);
TableModel *table_model = new TableModel(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
auto power_model = std::make_shared<InternalPowerModel>(table_model);
InternalPowerModels models{};
models[RiseFall::riseIndex()] = power_model;
InternalPower pwr(nullptr, nullptr, nullptr, nullptr, models);
EXPECT_EQ(pwr.model(RiseFall::rise()), power_model.get());
EXPECT_EQ(pwr.model(RiseFall::fall()), nullptr);
}
////////////////////////////////////////////////////////////////
// TimingArcAttrs additional coverage
////////////////////////////////////////////////////////////////
TEST(TimingArcAttrsTest, SetCond) {
TimingArcAttrs attrs;
FuncExpr *cond = FuncExpr::makeOne();
attrs.setCond(cond);
EXPECT_EQ(attrs.cond(), cond);
// Destructor cleans up cond
}
TEST(TimingArcAttrsTest, SetModel) {
TimingArcAttrs attrs;
// Models are initially null
EXPECT_EQ(attrs.model(RiseFall::rise()), nullptr);
EXPECT_EQ(attrs.model(RiseFall::fall()), nullptr);
}
TEST(TimingArcAttrsTest, DestructorCleanup) {
// Create attrs on heap and verify destructor cleans up properly
TimingArcAttrs *attrs = new TimingArcAttrs();
FuncExpr *cond = FuncExpr::makeZero();
attrs->setCond(cond);
attrs->setSdfCond("A==1");
attrs->setSdfCondStart("start");
attrs->setSdfCondEnd("end");
attrs->setModeName("mode1");
attrs->setModeValue("val1");
EXPECT_EQ(attrs->cond(), cond);
EXPECT_FALSE(attrs->sdfCond().empty());
EXPECT_FALSE(attrs->sdfCondStart().empty());
EXPECT_FALSE(attrs->sdfCondEnd().empty());
EXPECT_EQ(attrs->modeName(), "mode1");
EXPECT_EQ(attrs->modeValue(), "val1");
// Destructor should clean up cond, sdf strings, mode strings
delete attrs;
// If we get here without crash, cleanup succeeded
}
////////////////////////////////////////////////////////////////
// Table3 test - basic construction and value lookup
////////////////////////////////////////////////////////////////
TEST(Table3Test, BasicConstruction) {
// Table3 extends Table2: values_ is FloatTable* (Vector<FloatSeq*>)
// Layout: values_[axis1_idx * axis2_size + axis2_idx][axis3_idx]
// For a 2x2x2 table: 4 rows of 2 elements each
FloatSeq ax1_vals;
ax1_vals.push_back(0.1f); ax1_vals.push_back(0.5f);
FloatSeq ax2_vals;
ax2_vals.push_back(1.0f); ax2_vals.push_back(2.0f);
FloatSeq ax3_vals;
ax3_vals.push_back(10.0f); ax3_vals.push_back(20.0f);
auto axis1 = std::make_shared<TableAxis>(TableAxisVariable::input_transition_time, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(TableAxisVariable::total_output_net_capacitance, std::move(ax2_vals));
auto axis3 = std::make_shared<TableAxis>(TableAxisVariable::related_pin_transition, std::move(ax3_vals));
// 2x2x2: values_[axis1*axis2_size + axis2][axis3]
// row0 = (0,0) -> {1,2}, row1 = (0,1) -> {3,4}, row2 = (1,0) -> {5,6}, row3 = (1,1) -> {7,8}
FloatTable values;
FloatSeq row0; row0.push_back(1.0f); row0.push_back(2.0f);
FloatSeq row1; row1.push_back(3.0f); row1.push_back(4.0f);
FloatSeq row2; row2.push_back(5.0f); row2.push_back(6.0f);
FloatSeq row3; row3.push_back(7.0f); row3.push_back(8.0f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
values.push_back(std::move(row2)); values.push_back(std::move(row3));
Table tbl(std::move(values), axis1, axis2, axis3);
EXPECT_EQ(tbl.order(), 3);
EXPECT_NE(tbl.axis1(), nullptr);
EXPECT_NE(tbl.axis2(), nullptr);
EXPECT_NE(tbl.axis3(), nullptr);
// Check corner values
EXPECT_FLOAT_EQ(tbl.value(0, 0, 0), 1.0f);
EXPECT_FLOAT_EQ(tbl.value(1, 1, 1), 8.0f);
}
TEST(Table3Test, FindValue) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.1f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.1f); ax2_vals.push_back(1.0f);
FloatSeq ax3_vals;
ax3_vals.push_back(0.1f); ax3_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(TableAxisVariable::input_transition_time, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(TableAxisVariable::total_output_net_capacitance, std::move(ax2_vals));
auto axis3 = std::make_shared<TableAxis>(TableAxisVariable::related_pin_transition, std::move(ax3_vals));
// All values 1.0 in a 2x2x2 table (4 rows of 2)
FloatTable values;
for (int i = 0; i < 4; i++) {
FloatSeq row;
row.push_back(1.0f); row.push_back(1.0f);
values.push_back(std::move(row));
}
Table tbl(std::move(values), axis1, axis2, axis3);
// All values are 1.0, so any lookup should return ~1.0
float result = tbl.findValue(0.5f, 0.5f, 0.5f);
EXPECT_FLOAT_EQ(result, 1.0f);
}
////////////////////////////////////////////////////////////////
// TableModel wrapper tests
////////////////////////////////////////////////////////////////
TEST(TableModelTest, Order0) {
TablePtr tbl = std::make_shared<Table>(42.0f);
TableModel model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_EQ(model.order(), 0);
}
TEST(TableModelTest, Order1) {
FloatSeq axis_values;
axis_values.push_back(0.1f); axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(TableAxisVariable::input_transition_time, std::move(axis_values));
FloatSeq values;
values.push_back(1.0f); values.push_back(2.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_EQ(model.order(), 1);
EXPECT_NE(model.axis1(), nullptr);
EXPECT_EQ(model.axis2(), nullptr);
EXPECT_EQ(model.axis3(), nullptr);
}
TEST(TableModelTest, Order2) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.1f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.1f); ax2_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(TableAxisVariable::input_transition_time, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(TableAxisVariable::total_output_net_capacitance, std::move(ax2_vals));
FloatTable values;
FloatSeq row0; row0.push_back(1.0f); row0.push_back(2.0f);
FloatSeq row1; row1.push_back(3.0f); row1.push_back(4.0f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
TablePtr tbl = std::make_shared<Table>(std::move(values), axis1, axis2);
TableModel model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_EQ(model.order(), 2);
EXPECT_NE(model.axis1(), nullptr);
EXPECT_NE(model.axis2(), nullptr);
EXPECT_EQ(model.axis3(), nullptr);
}
////////////////////////////////////////////////////////////////
// Wireload additional tests
////////////////////////////////////////////////////////////////
TEST(WireloadTest, BasicConstruction) {
LibertyLibrary lib("test_lib", "test.lib");
Wireload wl("test_wl", &lib, 0.0f, 1.0f, 2.0f, 3.0f);
EXPECT_STREQ(wl.name(), "test_wl");
}
TEST(WireloadTest, SimpleConstructor) {
LibertyLibrary lib("test_lib", "test.lib");
Wireload wl("test_wl", &lib);
EXPECT_STREQ(wl.name(), "test_wl");
// Set individual properties
wl.setArea(10.0f);
wl.setResistance(1.5f);
wl.setCapacitance(2.5f);
wl.setSlope(0.5f);
}
TEST(WireloadTest, AddFanoutLength) {
LibertyLibrary lib("test_lib", "test.lib");
Wireload wl("test_wl", &lib, 0.0f, 1.0f, 1.0f, 0.5f);
wl.addFanoutLength(1, 10.0f);
wl.addFanoutLength(2, 20.0f);
wl.addFanoutLength(4, 40.0f);
float cap, res;
// Exact fanout match (first entry)
wl.findWireload(1.0f, nullptr, cap, res);
EXPECT_GT(cap, 0.0f);
EXPECT_GT(res, 0.0f);
// Between entries (interpolation)
wl.findWireload(3.0f, nullptr, cap, res);
EXPECT_GT(cap, 0.0f);
// Beyond max fanout (extrapolation)
wl.findWireload(5.0f, nullptr, cap, res);
EXPECT_GT(cap, 0.0f);
// Below min fanout (extrapolation)
wl.findWireload(0.5f, nullptr, cap, res);
// Result may be non-negative
}
TEST(WireloadTest, EmptyFanoutLengths) {
LibertyLibrary lib("test_lib", "test.lib");
Wireload wl("test_wl", &lib, 0.0f, 1.0f, 1.0f, 0.0f);
// No fanout lengths added
float cap, res;
wl.findWireload(1.0f, nullptr, cap, res);
// With no fanout lengths, length=0 so cap and res should be 0
EXPECT_FLOAT_EQ(cap, 0.0f);
EXPECT_FLOAT_EQ(res, 0.0f);
}
TEST(WireloadTest, UnsortedFanoutLengths) {
LibertyLibrary lib("test_lib", "test.lib");
Wireload wl("test_wl", &lib, 0.0f, 1.0f, 1.0f, 0.0f);
// Add in reverse order to exercise sorting
wl.addFanoutLength(4, 40.0f);
wl.addFanoutLength(2, 20.0f);
wl.addFanoutLength(1, 10.0f);
float cap, res;
wl.findWireload(1.0f, nullptr, cap, res);
EXPECT_GT(cap, 0.0f);
}
////////////////////////////////////////////////////////////////
// FuncExpr additional coverage
////////////////////////////////////////////////////////////////
TEST(FuncExprTest, PortTimingSensePositiveUnate) {
// Create port expression
ConcreteLibrary lib("test_lib", "test.lib", false);
ConcreteCell *cell = lib.makeCell("INV", true, "");
ConcretePort *a = cell->makePort("A");
LibertyPort *port = reinterpret_cast<LibertyPort*>(a);
FuncExpr *port_expr = FuncExpr::makePort(port);
// A port expression itself should be positive_unate for the same port
TimingSense sense = port_expr->portTimingSense(port);
EXPECT_EQ(sense, TimingSense::positive_unate);
delete port_expr;
}
TEST(FuncExprTest, NotTimingSenseNegativeUnate) {
ConcreteLibrary lib("test_lib", "test.lib", false);
ConcreteCell *cell = lib.makeCell("INV", true, "");
ConcretePort *a = cell->makePort("A");
LibertyPort *port = reinterpret_cast<LibertyPort*>(a);
FuncExpr *port_expr = FuncExpr::makePort(port);
FuncExpr *not_expr = FuncExpr::makeNot(port_expr);
// NOT(A) should be negative_unate for A
TimingSense sense = not_expr->portTimingSense(port);
EXPECT_EQ(sense, TimingSense::negative_unate);
delete not_expr;
}
////////////////////////////////////////////////////////////////
// LibertyLibrary property tests
////////////////////////////////////////////////////////////////
TEST(LibertyLibraryTest, NominalValues) {
LibertyLibrary lib("test_lib", "test.lib");
lib.setNominalProcess(1.0f);
lib.setNominalVoltage(1.2f);
lib.setNominalTemperature(25.0f);
EXPECT_FLOAT_EQ(lib.nominalProcess(), 1.0f);
EXPECT_FLOAT_EQ(lib.nominalVoltage(), 1.2f);
EXPECT_FLOAT_EQ(lib.nominalTemperature(), 25.0f);
}
TEST(LibertyLibraryTest, DelayModelType) {
LibertyLibrary lib("test_lib", "test.lib");
EXPECT_EQ(lib.delayModelType(), DelayModelType::table);
lib.setDelayModelType(DelayModelType::cmos_linear);
EXPECT_EQ(lib.delayModelType(), DelayModelType::cmos_linear);
}
TEST(LibertyLibraryTest, DefaultPinCaps) {
LibertyLibrary lib("test_lib", "test.lib");
lib.setDefaultInputPinCap(0.01f);
lib.setDefaultOutputPinCap(0.02f);
lib.setDefaultBidirectPinCap(0.015f);
EXPECT_FLOAT_EQ(lib.defaultInputPinCap(), 0.01f);
EXPECT_FLOAT_EQ(lib.defaultOutputPinCap(), 0.02f);
EXPECT_FLOAT_EQ(lib.defaultBidirectPinCap(), 0.015f);
}
TEST(LibertyLibraryTest, DefaultMaxCapacitance) {
LibertyLibrary lib("test_lib", "test.lib");
float cap;
bool exists;
lib.defaultMaxCapacitance(cap, exists);
EXPECT_FALSE(exists);
lib.setDefaultMaxCapacitance(5.0f);
lib.defaultMaxCapacitance(cap, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(cap, 5.0f);
}
TEST(LibertyLibraryTest, DefaultFanoutLoad) {
LibertyLibrary lib("test_lib", "test.lib");
float load;
bool exists;
lib.defaultFanoutLoad(load, exists);
EXPECT_FALSE(exists);
lib.setDefaultFanoutLoad(1.5f);
lib.defaultFanoutLoad(load, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(load, 1.5f);
}
TEST(LibertyLibraryTest, DefaultIntrinsic) {
LibertyLibrary lib("test_lib", "test.lib");
float intrinsic;
bool exists;
lib.defaultIntrinsic(RiseFall::rise(), intrinsic, exists);
EXPECT_FALSE(exists);
lib.setDefaultIntrinsic(RiseFall::rise(), 0.5f);
lib.defaultIntrinsic(RiseFall::rise(), intrinsic, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(intrinsic, 0.5f);
}
TEST(LibertyLibraryTest, WireSlewDegradationTable) {
LibertyLibrary lib("test_lib", "test.lib");
// Initially no wire slew degradation table
EXPECT_EQ(lib.wireSlewDegradationTable(RiseFall::rise()), nullptr);
EXPECT_EQ(lib.wireSlewDegradationTable(RiseFall::fall()), nullptr);
// Set a simple order-0 table (scalar)
TablePtr tbl = std::make_shared<Table>(0.1f);
TableModel *model = new TableModel(tbl, nullptr,
ScaleFactorType::transition,
RiseFall::rise());
lib.setWireSlewDegradationTable(model, RiseFall::rise());
EXPECT_NE(lib.wireSlewDegradationTable(RiseFall::rise()), nullptr);
// degradeWireSlew with order-0 table returns the constant
float result = lib.degradeWireSlew(RiseFall::rise(), 0.5f, 0.1f);
EXPECT_FLOAT_EQ(result, 0.1f);
// Fall should still return input slew (no table)
float result_fall = lib.degradeWireSlew(RiseFall::fall(), 0.5f, 0.1f);
EXPECT_FLOAT_EQ(result_fall, 0.5f);
}
TEST(LibertyLibraryTest, WireSlewDegradationOrder1) {
LibertyLibrary lib("test_lib", "test.lib");
// Create order-1 table with output_pin_transition axis
FloatSeq axis_values;
axis_values.push_back(0.1f);
axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::output_pin_transition, std::move(axis_values));
FloatSeq values;
values.push_back(0.1f);
values.push_back(1.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel *model = new TableModel(tbl, nullptr,
ScaleFactorType::transition,
RiseFall::rise());
lib.setWireSlewDegradationTable(model, RiseFall::rise());
float result = lib.degradeWireSlew(RiseFall::rise(), 0.5f, 0.1f);
// Should interpolate between 0.1 and 1.0 at slew=0.5
EXPECT_GT(result, 0.0f);
EXPECT_LT(result, 2.0f);
}
TEST(LibertyLibraryTest, Units) {
LibertyLibrary lib("test_lib", "test.lib");
const Units *units = lib.units();
EXPECT_NE(units, nullptr);
EXPECT_NE(units->timeUnit(), nullptr);
EXPECT_NE(units->capacitanceUnit(), nullptr);
EXPECT_NE(units->resistanceUnit(), nullptr);
}
////////////////////////////////////////////////////////////////
// Table report and additional tests
////////////////////////////////////////////////////////////////
TEST_F(LinearModelTest, Table0ReportValue) {
Table tbl(42.0f);
const Units *units = lib_->units();
std::string report = tbl.reportValue("Delay", cell_, nullptr,
0.0f, nullptr, 0.0f, 0.0f,
units->timeUnit(), 3);
EXPECT_FALSE(report.empty());
EXPECT_NE(report.find("Delay"), std::string::npos);
}
TEST_F(LinearModelTest, Table1ReportValue) {
FloatSeq axis_values;
axis_values.push_back(0.1f);
axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(axis_values));
FloatSeq values;
values.push_back(1.0f);
values.push_back(2.0f);
Table tbl(std::move(values), axis);
const Units *units = lib_->units();
std::string report = tbl.reportValue("Delay", cell_, nullptr,
0.5f, nullptr, 0.0f, 0.0f,
units->timeUnit(), 3);
EXPECT_FALSE(report.empty());
EXPECT_NE(report.find("Delay"), std::string::npos);
}
TEST_F(LinearModelTest, Table2ReportValue) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.1f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.1f); ax2_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(ax2_vals));
FloatTable values;
FloatSeq row0; row0.push_back(1.0f); row0.push_back(2.0f);
FloatSeq row1; row1.push_back(3.0f); row1.push_back(4.0f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
Table tbl(std::move(values), axis1, axis2);
const Units *units = lib_->units();
std::string report = tbl.reportValue("Delay", cell_, nullptr,
0.5f, nullptr, 0.5f, 0.0f,
units->timeUnit(), 3);
EXPECT_FALSE(report.empty());
EXPECT_NE(report.find("Delay"), std::string::npos);
}
TEST_F(LinearModelTest, Table3ReportValue) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.1f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.1f); ax2_vals.push_back(1.0f);
FloatSeq ax3_vals;
ax3_vals.push_back(0.1f); ax3_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(ax2_vals));
auto axis3 = std::make_shared<TableAxis>(
TableAxisVariable::related_pin_transition, std::move(ax3_vals));
FloatTable values;
for (int i = 0; i < 4; i++) {
FloatSeq row;
row.push_back(1.0f + i); row.push_back(2.0f + i);
values.push_back(std::move(row));
}
Table tbl(std::move(values), axis1, axis2, axis3);
const Units *units = lib_->units();
std::string report = tbl.reportValue("Delay", cell_, nullptr,
0.5f, nullptr, 0.5f, 0.5f,
units->timeUnit(), 3);
EXPECT_FALSE(report.empty());
EXPECT_NE(report.find("Delay"), std::string::npos);
}
TEST_F(LinearModelTest, TableModelReport) {
TablePtr tbl = std::make_shared<Table>(42.0f);
TableModel model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
const Units *units = lib_->units();
Report *report_obj = nullptr;
// report needs Report*; test order/axes instead
EXPECT_EQ(model.order(), 0);
EXPECT_EQ(model.axis1(), nullptr);
EXPECT_EQ(model.axis2(), nullptr);
EXPECT_EQ(model.axis3(), nullptr);
}
TEST_F(LinearModelTest, TableModelFindValue) {
FloatSeq axis_values;
axis_values.push_back(0.1f);
axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(axis_values));
FloatSeq values;
values.push_back(10.0f);
values.push_back(20.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
float result = model.findValue(0.5f, 0.0f, 0.0f);
EXPECT_GT(result, 10.0f);
EXPECT_LT(result, 20.0f);
}
TEST_F(LinearModelTest, TableModelReportValue) {
FloatSeq axis_values;
axis_values.push_back(0.1f);
axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(axis_values));
FloatSeq values;
values.push_back(10.0f);
values.push_back(20.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
const Units *units = lib_->units();
std::string report = model.reportValue("Delay", cell_, nullptr,
0.5f, nullptr, 0.0f, 0.0f,
units->timeUnit(), 3);
EXPECT_FALSE(report.empty());
EXPECT_NE(report.find("Delay"), std::string::npos);
}
////////////////////////////////////////////////////////////////
// FuncExpr additional operators
////////////////////////////////////////////////////////////////
TEST(FuncExprTest, AndTimingSense) {
ConcreteLibrary lib("test_lib", "test.lib", false);
ConcreteCell *cell = lib.makeCell("AND2", true, "");
ConcretePort *a = cell->makePort("A");
ConcretePort *b = cell->makePort("B");
LibertyPort *port_a = reinterpret_cast<LibertyPort*>(a);
LibertyPort *port_b = reinterpret_cast<LibertyPort*>(b);
FuncExpr *expr_a = FuncExpr::makePort(port_a);
FuncExpr *expr_b = FuncExpr::makePort(port_b);
FuncExpr *and_expr = FuncExpr::makeAnd(expr_a, expr_b);
// A AND B should be positive_unate for A
TimingSense sense = and_expr->portTimingSense(port_a);
EXPECT_EQ(sense, TimingSense::positive_unate);
delete and_expr;
}
TEST(FuncExprTest, OrTimingSense) {
ConcreteLibrary lib("test_lib", "test.lib", false);
ConcreteCell *cell = lib.makeCell("OR2", true, "");
ConcretePort *a = cell->makePort("A");
ConcretePort *b = cell->makePort("B");
LibertyPort *port_a = reinterpret_cast<LibertyPort*>(a);
LibertyPort *port_b = reinterpret_cast<LibertyPort*>(b);
FuncExpr *expr_a = FuncExpr::makePort(port_a);
FuncExpr *expr_b = FuncExpr::makePort(port_b);
FuncExpr *or_expr = FuncExpr::makeOr(expr_a, expr_b);
TimingSense sense = or_expr->portTimingSense(port_a);
EXPECT_EQ(sense, TimingSense::positive_unate);
delete or_expr;
}
TEST(FuncExprTest, XorTimingSense) {
ConcreteLibrary lib("test_lib", "test.lib", false);
ConcreteCell *cell = lib.makeCell("XOR2", true, "");
ConcretePort *a = cell->makePort("A");
ConcretePort *b = cell->makePort("B");
LibertyPort *port_a = reinterpret_cast<LibertyPort*>(a);
LibertyPort *port_b = reinterpret_cast<LibertyPort*>(b);
FuncExpr *expr_a = FuncExpr::makePort(port_a);
FuncExpr *expr_b = FuncExpr::makePort(port_b);
FuncExpr *xor_expr = FuncExpr::makeXor(expr_a, expr_b);
// XOR should be non_unate
TimingSense sense = xor_expr->portTimingSense(port_a);
EXPECT_EQ(sense, TimingSense::non_unate);
delete xor_expr;
}
TEST(FuncExprTest, ZeroOneExpressions) {
FuncExpr *zero = FuncExpr::makeZero();
FuncExpr *one = FuncExpr::makeOne();
EXPECT_NE(zero, nullptr);
EXPECT_NE(one, nullptr);
delete zero;
delete one;
}
////////////////////////////////////////////////////////////////
// Sequential tests
////////////////////////////////////////////////////////////////
TEST(SequentialTest, BasicConstruction) {
// Sequential class is constructed and used during liberty parsing
// We can test the StringTableAxisVariable utility
const char *var_str = tableVariableString(TableAxisVariable::input_transition_time);
EXPECT_STREQ(var_str, "input_transition_time");
var_str = tableVariableString(TableAxisVariable::total_output_net_capacitance);
EXPECT_STREQ(var_str, "total_output_net_capacitance");
}
TEST(TableAxisVariableTest, StringToVariable) {
TableAxisVariable var = stringTableAxisVariable("input_transition_time");
EXPECT_EQ(var, TableAxisVariable::input_transition_time);
var = stringTableAxisVariable("total_output_net_capacitance");
EXPECT_EQ(var, TableAxisVariable::total_output_net_capacitance);
var = stringTableAxisVariable("related_pin_transition");
EXPECT_EQ(var, TableAxisVariable::related_pin_transition);
}
////////////////////////////////////////////////////////////////
// WireloadSelection tests
////////////////////////////////////////////////////////////////
TEST(WireloadSelectionTest, BasicConstruction) {
WireloadSelection sel("test_sel");
EXPECT_STREQ(sel.name(), "test_sel");
}
TEST(WireloadSelectionTest, FindWireload) {
LibertyLibrary lib("test_lib", "test.lib");
Wireload wl1("small", &lib, 0.0f, 1.0f, 1.0f, 0.5f);
Wireload wl2("large", &lib, 0.0f, 2.0f, 2.0f, 1.0f);
WireloadSelection sel("test_sel");
sel.addWireloadFromArea(0.0f, 100.0f, &wl1);
sel.addWireloadFromArea(100.0f, 1000.0f, &wl2);
const Wireload *found = sel.findWireload(50.0f);
EXPECT_EQ(found, &wl1);
found = sel.findWireload(500.0f);
EXPECT_EQ(found, &wl2);
}
////////////////////////////////////////////////////////////////
// Table utility functions
////////////////////////////////////////////////////////////////
TEST(TableUtilTest, WireloadTreeString) {
EXPECT_STREQ(wireloadTreeString(WireloadTree::worst_case), "worst_case_tree");
EXPECT_STREQ(wireloadTreeString(WireloadTree::best_case), "best_case_tree");
EXPECT_STREQ(wireloadTreeString(WireloadTree::balanced), "balanced_tree");
}
TEST(TableUtilTest, StringWireloadTree) {
EXPECT_EQ(stringWireloadTree("worst_case_tree"), WireloadTree::worst_case);
EXPECT_EQ(stringWireloadTree("best_case_tree"), WireloadTree::best_case);
EXPECT_EQ(stringWireloadTree("balanced_tree"), WireloadTree::balanced);
EXPECT_EQ(stringWireloadTree("invalid"), WireloadTree::unknown);
}
TEST(TableUtilTest, WireloadModeString) {
EXPECT_STREQ(wireloadModeString(WireloadMode::top), "top");
EXPECT_STREQ(wireloadModeString(WireloadMode::enclosed), "enclosed");
EXPECT_STREQ(wireloadModeString(WireloadMode::segmented), "segmented");
}
TEST(TableUtilTest, StringWireloadMode) {
EXPECT_EQ(stringWireloadMode("top"), WireloadMode::top);
EXPECT_EQ(stringWireloadMode("enclosed"), WireloadMode::enclosed);
EXPECT_EQ(stringWireloadMode("segmented"), WireloadMode::segmented);
}
////////////////////////////////////////////////////////////////
// LibertyLibrary wireload & operating conditions tests
////////////////////////////////////////////////////////////////
TEST(LibertyLibraryTest, MakeAndFindWireload) {
LibertyLibrary lib("test_lib", "test.lib");
Wireload *wl = lib.makeWireload("test_wl");
EXPECT_NE(wl, nullptr);
const Wireload *found = lib.findWireload("test_wl");
EXPECT_EQ(found, wl);
EXPECT_EQ(lib.findWireload("nonexistent"), nullptr);
}
TEST(LibertyLibraryTest, DefaultWireload) {
LibertyLibrary lib("test_lib", "test.lib");
EXPECT_EQ(lib.defaultWireload(), nullptr);
Wireload *wl = lib.makeWireload("default_wl");
lib.setDefaultWireload(wl);
EXPECT_EQ(lib.defaultWireload(), wl);
}
TEST(LibertyLibraryTest, WireloadSelection) {
LibertyLibrary lib("test_lib", "test.lib");
WireloadSelection *sel = lib.makeWireloadSelection("test_sel");
EXPECT_NE(sel, nullptr);
EXPECT_EQ(lib.findWireloadSelection("test_sel"), sel);
EXPECT_EQ(lib.findWireloadSelection("nonexistent"), nullptr);
}
TEST(LibertyLibraryTest, DefaultWireloadSelection) {
LibertyLibrary lib("test_lib", "test.lib");
EXPECT_EQ(lib.defaultWireloadSelection(), nullptr);
WireloadSelection *sel = lib.makeWireloadSelection("test_sel");
lib.setDefaultWireloadSelection(sel);
EXPECT_EQ(lib.defaultWireloadSelection(), sel);
}
TEST(LibertyLibraryTest, DefaultWireloadMode) {
LibertyLibrary lib("test_lib", "test.lib");
lib.setDefaultWireloadMode(WireloadMode::top);
EXPECT_EQ(lib.defaultWireloadMode(), WireloadMode::top);
lib.setDefaultWireloadMode(WireloadMode::enclosed);
EXPECT_EQ(lib.defaultWireloadMode(), WireloadMode::enclosed);
}
TEST(LibertyLibraryTest, Thresholds) {
LibertyLibrary lib("test_lib", "test.lib");
lib.setInputThreshold(RiseFall::rise(), 0.5f);
lib.setInputThreshold(RiseFall::fall(), 0.5f);
EXPECT_FLOAT_EQ(lib.inputThreshold(RiseFall::rise()), 0.5f);
EXPECT_FLOAT_EQ(lib.inputThreshold(RiseFall::fall()), 0.5f);
lib.setOutputThreshold(RiseFall::rise(), 0.5f);
lib.setOutputThreshold(RiseFall::fall(), 0.5f);
EXPECT_FLOAT_EQ(lib.outputThreshold(RiseFall::rise()), 0.5f);
EXPECT_FLOAT_EQ(lib.outputThreshold(RiseFall::fall()), 0.5f);
lib.setSlewLowerThreshold(RiseFall::rise(), 0.2f);
lib.setSlewUpperThreshold(RiseFall::rise(), 0.8f);
lib.setSlewLowerThreshold(RiseFall::fall(), 0.2f);
lib.setSlewUpperThreshold(RiseFall::fall(), 0.8f);
EXPECT_FLOAT_EQ(lib.slewLowerThreshold(RiseFall::rise()), 0.2f);
EXPECT_FLOAT_EQ(lib.slewUpperThreshold(RiseFall::rise()), 0.8f);
EXPECT_FLOAT_EQ(lib.slewLowerThreshold(RiseFall::fall()), 0.2f);
EXPECT_FLOAT_EQ(lib.slewUpperThreshold(RiseFall::fall()), 0.8f);
}
TEST(LibertyLibraryTest, SlewDerateFromLibrary) {
LibertyLibrary lib("test_lib", "test.lib");
// Default derate is 1.0
EXPECT_FLOAT_EQ(lib.slewDerateFromLibrary(), 1.0f);
// Set custom derate
lib.setSlewDerateFromLibrary(1.667f);
EXPECT_FLOAT_EQ(lib.slewDerateFromLibrary(), 1.667f);
}
TEST(LibertyLibraryTest, DefaultPinResistance) {
LibertyLibrary lib("test_lib", "test.lib");
float res;
bool exists;
lib.defaultOutputPinRes(RiseFall::rise(), res, exists);
EXPECT_FALSE(exists);
lib.setDefaultOutputPinRes(RiseFall::rise(), 10.0f);
lib.defaultOutputPinRes(RiseFall::rise(), res, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(res, 10.0f);
lib.setDefaultBidirectPinRes(RiseFall::rise(), 15.0f);
lib.defaultBidirectPinRes(RiseFall::rise(), res, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(res, 15.0f);
}
TEST(LibertyLibraryTest, ScaleFactor) {
LibertyLibrary lib("test_lib", "test.lib");
// With no scale factors set, should return 1.0
float sf = lib.scaleFactor(ScaleFactorType::cell, nullptr);
EXPECT_FLOAT_EQ(sf, 1.0f);
}
TEST(LibertyLibraryTest, DefaultMaxSlew) {
LibertyLibrary lib("test_lib", "test.lib");
float slew;
bool exists;
lib.defaultMaxSlew(slew, exists);
EXPECT_FALSE(exists);
lib.setDefaultMaxSlew(5.0f);
lib.defaultMaxSlew(slew, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(slew, 5.0f);
}
TEST(LibertyLibraryTest, DefaultMaxFanout) {
LibertyLibrary lib("test_lib", "test.lib");
float fanout;
bool exists;
lib.defaultMaxFanout(fanout, exists);
EXPECT_FALSE(exists);
lib.setDefaultMaxFanout(10.0f);
lib.defaultMaxFanout(fanout, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(fanout, 10.0f);
}
////////////////////////////////////////////////////////////////
// LibertyLibrary table template and bus type management
////////////////////////////////////////////////////////////////
TEST(LibertyLibraryTest, MakeAndFindTableTemplate) {
LibertyLibrary lib("test_lib", "test.lib");
TableTemplate *tmpl = lib.makeTableTemplate("delay_template",
TableTemplateType::delay);
EXPECT_NE(tmpl, nullptr);
TableTemplate *found = lib.findTableTemplate("delay_template",
TableTemplateType::delay);
EXPECT_EQ(found, tmpl);
EXPECT_EQ(lib.findTableTemplate("nonexistent", TableTemplateType::delay),
nullptr);
}
TEST(LibertyLibraryTest, MakeAndFindBusDcl) {
LibertyLibrary lib("test_lib", "test.lib");
BusDcl *bus = lib.makeBusDcl("data_bus", 7, 0);
EXPECT_NE(bus, nullptr);
BusDcl *found = lib.findBusDcl("data_bus");
EXPECT_EQ(found, bus);
EXPECT_EQ(lib.findBusDcl("nonexistent"), nullptr);
}
////////////////////////////////////////////////////////////////
// Table2 findValue test
////////////////////////////////////////////////////////////////
TEST(Table2Test, FindValueInterpolation) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.0f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.0f); ax2_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(ax2_vals));
FloatTable values;
FloatSeq row0; row0.push_back(1.0f); row0.push_back(3.0f);
FloatSeq row1; row1.push_back(5.0f); row1.push_back(7.0f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
Table tbl(std::move(values), axis1, axis2);
// Center should be average of all corners: (1+3+5+7)/4 = 4
float center = tbl.findValue(0.5f, 0.5f, 0.0f);
EXPECT_NEAR(center, 4.0f, 0.01f);
// Corner values
EXPECT_FLOAT_EQ(tbl.findValue(0.0f, 0.0f, 0.0f), 1.0f);
EXPECT_FLOAT_EQ(tbl.findValue(1.0f, 1.0f, 0.0f), 7.0f);
}
////////////////////////////////////////////////////////////////
// GateTableModel static method (checkAxes)
////////////////////////////////////////////////////////////////
TEST(GateTableModelTest, CheckAxesOrder0) {
TablePtr tbl = std::make_shared<Table>(1.0f);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_TRUE(GateTableModel::checkAxes(&tbl_model));
}
TEST(GateTableModelTest, CheckAxesOrder1) {
FloatSeq axis_values;
axis_values.push_back(0.1f); axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(axis_values));
FloatSeq values;
values.push_back(1.0f); values.push_back(2.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_TRUE(GateTableModel::checkAxes(&tbl_model));
}
TEST(GateTableModelTest, CheckAxesOrder2) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.1f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.1f); ax2_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(ax2_vals));
FloatTable values;
FloatSeq row0; row0.push_back(1.0f); row0.push_back(2.0f);
FloatSeq row1; row1.push_back(3.0f); row1.push_back(4.0f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
TablePtr tbl = std::make_shared<Table>(std::move(values), axis1, axis2);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_TRUE(GateTableModel::checkAxes(&tbl_model));
}
////////////////////////////////////////////////////////////////
// CheckSlewDegradationAxes
////////////////////////////////////////////////////////////////
TEST(LibertyLibraryTest, CheckSlewDegradationAxesOrder0) {
TablePtr tbl = std::make_shared<Table>(1.0f);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_TRUE(LibertyLibrary::checkSlewDegradationAxes(&tbl_model));
}
TEST(LibertyLibraryTest, CheckSlewDegradationAxesOrder1) {
FloatSeq axis_values;
axis_values.push_back(0.1f); axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::output_pin_transition, std::move(axis_values));
FloatSeq values;
values.push_back(0.1f); values.push_back(1.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_TRUE(LibertyLibrary::checkSlewDegradationAxes(&tbl_model));
}
////////////////////////////////////////////////////////////////
// InternalPower additional
////////////////////////////////////////////////////////////////
TEST(InternalPowerTest, RelatedPgPinViaConstruction) {
InternalPowerModels models{};
// relatedPgPin is now set via constructor
InternalPower pwr(nullptr, nullptr, nullptr, nullptr, models);
EXPECT_EQ(pwr.relatedPgPin(), nullptr);
}
////////////////////////////////////////////////////////////////
// TimingArc set/model tests
////////////////////////////////////////////////////////////////
TEST(TimingArcAttrsTest, SdfCondStrings) {
TimingArcAttrs attrs;
attrs.setSdfCond("A==1'b1");
EXPECT_EQ(attrs.sdfCond(), "A==1'b1");
attrs.setSdfCondStart("start_val");
EXPECT_EQ(attrs.sdfCondStart(), "start_val");
attrs.setSdfCondEnd("end_val");
EXPECT_EQ(attrs.sdfCondEnd(), "end_val");
}
TEST(TimingArcAttrsTest, ModeNameValue) {
TimingArcAttrs attrs;
attrs.setModeName("test_mode");
EXPECT_EQ(attrs.modeName(), "test_mode");
attrs.setModeValue("mode_val");
EXPECT_EQ(attrs.modeValue(), "mode_val");
}
////////////////////////////////////////////////////////////////
// Table0 value access
////////////////////////////////////////////////////////////////
TEST(Table0Test, ValueAccess) {
Table tbl(42.5f);
EXPECT_FLOAT_EQ(tbl.value(0, 0, 0), 42.5f);
EXPECT_FLOAT_EQ(tbl.value(1, 2, 3), 42.5f);
EXPECT_FLOAT_EQ(tbl.findValue(0.0f, 0.0f, 0.0f), 42.5f);
EXPECT_FLOAT_EQ(tbl.findValue(1.0f, 2.0f, 3.0f), 42.5f);
EXPECT_EQ(tbl.order(), 0);
}
////////////////////////////////////////////////////////////////
// TableModel with Table2 findValue
////////////////////////////////////////////////////////////////
TEST(TableModelTest, FindValueOrder2) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.0f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.0f); ax2_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(ax2_vals));
FloatTable values;
FloatSeq row0; row0.push_back(1.0f); row0.push_back(3.0f);
FloatSeq row1; row1.push_back(5.0f); row1.push_back(7.0f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
TablePtr tbl = std::make_shared<Table>(std::move(values), axis1, axis2);
TableModel model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
float center = model.findValue(0.5f, 0.5f, 0.0f);
EXPECT_NEAR(center, 4.0f, 0.01f);
}
////////////////////////////////////////////////////////////////
// ScaleFactors tests
////////////////////////////////////////////////////////////////
TEST(ScaleFactorsTest, BasicConstruction) {
ScaleFactors sf("test_scales");
EXPECT_STREQ(sf.name(), "test_scales");
}
TEST(ScaleFactorsTest, SetAndGetWithRiseFall) {
ScaleFactors sf("sf1");
sf.setScale(ScaleFactorType::cell, ScaleFactorPvt::process,
RiseFall::rise(), 1.5f);
sf.setScale(ScaleFactorType::cell, ScaleFactorPvt::process,
RiseFall::fall(), 2.0f);
EXPECT_FLOAT_EQ(sf.scale(ScaleFactorType::cell, ScaleFactorPvt::process,
RiseFall::rise()), 1.5f);
EXPECT_FLOAT_EQ(sf.scale(ScaleFactorType::cell, ScaleFactorPvt::process,
RiseFall::fall()), 2.0f);
}
TEST(ScaleFactorsTest, SetAndGetWithIndex) {
ScaleFactors sf("sf2");
sf.setScale(ScaleFactorType::cell, ScaleFactorPvt::volt,
RiseFall::rise(), 3.0f);
EXPECT_FLOAT_EQ(sf.scale(ScaleFactorType::cell, ScaleFactorPvt::volt,
RiseFall::riseIndex()), 3.0f);
}
TEST(ScaleFactorsTest, SetAndGetWithoutRiseFall) {
ScaleFactors sf("sf3");
sf.setScale(ScaleFactorType::cell, ScaleFactorPvt::temp, 4.0f);
EXPECT_FLOAT_EQ(sf.scale(ScaleFactorType::cell, ScaleFactorPvt::temp), 4.0f);
}
////////////////////////////////////////////////////////////////
// OcvDerate tests
////////////////////////////////////////////////////////////////
TEST(OcvDerateTest, BasicConstruction) {
OcvDerate derate(stringCopy("test_ocv"));
EXPECT_EQ(derate.name(), "test_ocv");
}
TEST(OcvDerateTest, SetAndGetDerateTable) {
OcvDerate derate(stringCopy("ocv1"));
TablePtr tbl = std::make_shared<Table>(0.95f);
derate.setDerateTable(RiseFall::rise(), EarlyLate::early(),
PathType::data, tbl);
const Table *found = derate.derateTable(RiseFall::rise(), EarlyLate::early(),
PathType::data);
EXPECT_NE(found, nullptr);
}
TEST(OcvDerateTest, NullByDefault) {
OcvDerate derate(stringCopy("ocv2"));
const Table *found = derate.derateTable(RiseFall::fall(), EarlyLate::late(),
PathType::clk);
EXPECT_EQ(found, nullptr);
}
////////////////////////////////////////////////////////////////
// LibertyLibrary OCV and supply voltage tests
////////////////////////////////////////////////////////////////
TEST(LibertyLibraryTest, OcvArcDepth) {
LibertyLibrary lib("test_lib", "test.lib");
lib.setOcvArcDepth(5.0f);
EXPECT_FLOAT_EQ(lib.ocvArcDepth(), 5.0f);
}
TEST(LibertyLibraryTest, DefaultOcvDerate) {
LibertyLibrary lib("test_lib", "test.lib");
EXPECT_EQ(lib.defaultOcvDerate(), nullptr);
OcvDerate *derate = new OcvDerate(stringCopy("default_ocv"));
lib.setDefaultOcvDerate(derate);
EXPECT_EQ(lib.defaultOcvDerate(), derate);
}
TEST(LibertyLibraryTest, MakeAndFindOcvDerate) {
LibertyLibrary lib("test_lib", "test.lib");
OcvDerate *derate = lib.makeOcvDerate("cell_ocv");
EXPECT_NE(derate, nullptr);
OcvDerate *found = lib.findOcvDerate("cell_ocv");
EXPECT_EQ(found, derate);
EXPECT_EQ(lib.findOcvDerate("nonexistent"), nullptr);
}
TEST(LibertyLibraryTest, SupplyVoltage) {
LibertyLibrary lib("test_lib", "test.lib");
float voltage;
bool exists;
lib.supplyVoltage("VDD", voltage, exists);
EXPECT_FALSE(exists);
lib.addSupplyVoltage("VDD", 1.1f);
lib.supplyVoltage("VDD", voltage, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(voltage, 1.1f);
EXPECT_TRUE(lib.supplyExists("VDD"));
EXPECT_FALSE(lib.supplyExists("VSS"));
}
TEST(LibertyLibraryTest, MakeAndFindScaleFactors) {
LibertyLibrary lib("test_lib", "test.lib");
ScaleFactors *sf = lib.makeScaleFactors("k_process");
EXPECT_NE(sf, nullptr);
ScaleFactors *found = lib.findScaleFactors("k_process");
EXPECT_EQ(found, sf);
}
TEST(LibertyLibraryTest, DefaultScaleFactors) {
ASSERT_NO_THROW(( [&](){
LibertyLibrary lib("test_lib", "test.lib");
ScaleFactors *sf = new ScaleFactors("default_sf");
lib.setScaleFactors(sf);
// Just verifying it doesn't crash - scale factors are used internally
}() ));
}
TEST(LibertyLibraryTest, MakeScaledCell) {
LibertyLibrary lib("test_lib", "test.lib");
LibertyCell *cell = lib.makeScaledCell("scaled_inv", "test.lib");
EXPECT_NE(cell, nullptr);
EXPECT_STREQ(cell->name(), "scaled_inv");
delete cell;
}
TEST(LibertyLibraryTest, DefaultPinResistanceWithDirection) {
PortDirection::init();
LibertyLibrary lib("test_lib", "test.lib");
float res;
bool exists;
// Test with output direction
lib.setDefaultOutputPinRes(RiseFall::rise(), 100.0f);
lib.defaultPinResistance(RiseFall::rise(), PortDirection::output(),
res, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(res, 100.0f);
// Test with tristate direction
lib.setDefaultBidirectPinRes(RiseFall::rise(), 200.0f);
lib.defaultPinResistance(RiseFall::rise(), PortDirection::tristate(),
res, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(res, 200.0f);
}
TEST(LibertyLibraryTest, TableTemplates) {
LibertyLibrary lib("test_lib", "test.lib");
lib.makeTableTemplate("tmpl1", TableTemplateType::delay);
lib.makeTableTemplate("tmpl2", TableTemplateType::power);
auto tbl_tmpls = lib.tableTemplates();
EXPECT_GE(tbl_tmpls.size(), 2u);
}
////////////////////////////////////////////////////////////////
// TestCell (LibertyCell) tests
////////////////////////////////////////////////////////////////
TEST(TestCellTest, BasicConstruction) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "INV_X1", "test.lib");
EXPECT_STREQ(cell.name(), "INV_X1");
EXPECT_EQ(cell.libertyLibrary(), &lib);
}
TEST(TestCellTest, SetArea) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "BUF_X1", "test.lib");
cell.setArea(2.5f);
EXPECT_FLOAT_EQ(cell.area(), 2.5f);
}
TEST(TestCellTest, SetDontUse) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "BUF_X1", "test.lib");
EXPECT_FALSE(cell.dontUse());
cell.setDontUse(true);
EXPECT_TRUE(cell.dontUse());
}
TEST(TestCellTest, SetIsMacro) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "SRAM", "test.lib");
cell.setIsMacro(true);
EXPECT_TRUE(cell.isMacro());
}
TEST(TestCellTest, SetIsPad) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "PAD1", "test.lib");
cell.setIsPad(true);
EXPECT_TRUE(cell.isPad());
}
TEST(TestCellTest, SetIsClockCell) {
ASSERT_NO_THROW(( [&](){
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CLKBUF", "test.lib");
cell.setIsClockCell(true);
// isClockCell is not directly queryable, but this covers the setter
}() ));
}
TEST(TestCellTest, SetIsLevelShifter) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "LS1", "test.lib");
cell.setIsLevelShifter(true);
EXPECT_TRUE(cell.isLevelShifter());
}
TEST(TestCellTest, SetLevelShifterType) {
ASSERT_NO_THROW(( [&](){
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "LS2", "test.lib");
cell.setLevelShifterType(LevelShifterType::HL);
}() ));
}
TEST(TestCellTest, SetIsIsolationCell) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "ISO1", "test.lib");
cell.setIsIsolationCell(true);
EXPECT_TRUE(cell.isIsolationCell());
}
TEST(TestCellTest, SetSwitchCellType) {
ASSERT_NO_THROW(( [&](){
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "SW1", "test.lib");
cell.setSwitchCellType(SwitchCellType::coarse_grain);
}() ));
}
TEST(TestCellTest, SetInterfaceTiming) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
cell.setInterfaceTiming(true);
EXPECT_TRUE(cell.interfaceTiming());
}
TEST(TestCellTest, ClockGateTypes) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "ICG1", "test.lib");
EXPECT_FALSE(cell.isClockGate());
EXPECT_FALSE(cell.isClockGateLatchPosedge());
EXPECT_FALSE(cell.isClockGateLatchNegedge());
EXPECT_FALSE(cell.isClockGateOther());
cell.setClockGateType(ClockGateType::latch_posedge);
EXPECT_TRUE(cell.isClockGate());
EXPECT_TRUE(cell.isClockGateLatchPosedge());
EXPECT_FALSE(cell.isClockGateLatchNegedge());
cell.setClockGateType(ClockGateType::latch_negedge);
EXPECT_TRUE(cell.isClockGateLatchNegedge());
cell.setClockGateType(ClockGateType::other);
EXPECT_TRUE(cell.isClockGateOther());
}
TEST(TestCellTest, ModeDef) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
ModeDef *mode = cell.makeModeDef("test_mode");
EXPECT_NE(mode, nullptr);
EXPECT_EQ(mode->name(), "test_mode");
const ModeDef *found = cell.findModeDef("test_mode");
EXPECT_EQ(found, mode);
EXPECT_EQ(cell.findModeDef("nonexistent"), nullptr);
}
TEST(TestCellTest, CellScaleFactors) {
ASSERT_NO_THROW(( [&](){
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
ScaleFactors *sf = new ScaleFactors("cell_sf");
cell.setScaleFactors(sf);
// Scale factors are used internally during delay calculation
}() ));
}
TEST(TestCellTest, CellBusDcl) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
BusDcl *bus = cell.makeBusDcl("data", 7, 0);
BusDcl *found = cell.findBusDcl("data");
EXPECT_EQ(found, bus);
EXPECT_EQ(cell.findBusDcl("nonexistent"), nullptr);
}
TEST(TestCellTest, HasInternalPorts) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
EXPECT_FALSE(cell.hasInternalPorts());
}
TEST(TestCellTest, SetAlwaysOn) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "AON1", "test.lib");
cell.setAlwaysOn(true);
EXPECT_TRUE(cell.alwaysOn());
}
TEST(TestCellTest, SetIsMemory) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "MEM1", "test.lib");
cell.setIsMemory(true);
EXPECT_TRUE(cell.isMemory());
}
TEST(TestCellTest, CellOcvArcDepth) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
cell.setOcvArcDepth(3.0f);
EXPECT_FLOAT_EQ(cell.ocvArcDepth(), 3.0f);
}
TEST(TestCellTest, CellOcvDerate) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
// Without cell-level derate, returns library default
EXPECT_EQ(cell.ocvDerate(), nullptr);
OcvDerate *derate = new OcvDerate(stringCopy("cell_ocv"));
cell.setOcvDerate(derate);
EXPECT_EQ(cell.ocvDerate(), derate);
}
TEST(TestCellTest, CellAddFindOcvDerate) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
OcvDerate *derate = cell.makeOcvDerate("named_ocv");
OcvDerate *found = cell.findOcvDerate("named_ocv");
EXPECT_EQ(found, derate);
EXPECT_EQ(cell.findOcvDerate("nonexistent"), nullptr);
}
TEST(TestCellTest, LeakagePower) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
float leakage;
bool exists;
cell.leakagePower(leakage, exists);
EXPECT_FALSE(exists);
cell.setLeakagePower(0.001f);
cell.leakagePower(leakage, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(leakage, 0.001f);
}
TEST(TestCellTest, TimingArcSetCount) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
EXPECT_EQ(cell.timingArcSetCount(), 0u);
}
////////////////////////////////////////////////////////////////
// ScanSignalType tests
////////////////////////////////////////////////////////////////
TEST(ScanSignalTypeTest, Names) {
EXPECT_NE(scanSignalTypeName(ScanSignalType::enable), nullptr);
EXPECT_NE(scanSignalTypeName(ScanSignalType::enable_inverted), nullptr);
}
////////////////////////////////////////////////////////////////
// LibertyLibrary cell iteration tests
////////////////////////////////////////////////////////////////
TEST(LibertyCellIteratorTest, EmptyLibrary) {
LibertyLibrary lib("test_lib", "test.lib");
LibertyCellIterator iter(&lib);
EXPECT_FALSE(iter.hasNext());
}
////////////////////////////////////////////////////////////////
// checkSlewDegradationAxes Order2 test
////////////////////////////////////////////////////////////////
TEST(LibertyLibraryTest, CheckSlewDegradationAxesOrder2) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.0f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.0f); ax2_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::output_pin_transition, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::connect_delay, std::move(ax2_vals));
FloatTable values;
FloatSeq row0; row0.push_back(0.1f); row0.push_back(0.2f);
FloatSeq row1; row1.push_back(0.3f); row1.push_back(0.4f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
TablePtr tbl = std::make_shared<Table>(std::move(values), axis1, axis2);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_TRUE(LibertyLibrary::checkSlewDegradationAxes(&tbl_model));
}
TEST(LibertyLibraryTest, CheckSlewDegradationAxesOrder2Reversed) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.0f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.0f); ax2_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::connect_delay, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::output_pin_transition, std::move(ax2_vals));
FloatTable values;
FloatSeq row0; row0.push_back(0.1f); row0.push_back(0.2f);
FloatSeq row1; row1.push_back(0.3f); row1.push_back(0.4f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
TablePtr tbl = std::make_shared<Table>(std::move(values), axis1, axis2);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_TRUE(LibertyLibrary::checkSlewDegradationAxes(&tbl_model));
}
////////////////////////////////////////////////////////////////
// TableTemplate axis tests
////////////////////////////////////////////////////////////////
TEST(TableTemplateTest, BasicConstruction) {
TableTemplate tmpl("delay_tmpl");
EXPECT_EQ(tmpl.name(), "delay_tmpl");
EXPECT_EQ(tmpl.axis1(), nullptr);
EXPECT_EQ(tmpl.axis2(), nullptr);
EXPECT_EQ(tmpl.axis3(), nullptr);
}
TEST(TableTemplateTest, ConstructionWithAxes) {
FloatSeq vals1;
vals1.push_back(0.1f); vals1.push_back(1.0f);
FloatSeq vals2;
vals2.push_back(0.01f); vals2.push_back(0.1f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(vals1));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(vals2));
TableTemplate tmpl("delay_2d", axis1, axis2, nullptr);
EXPECT_EQ(tmpl.name(), "delay_2d");
EXPECT_NE(tmpl.axis1(), nullptr);
EXPECT_NE(tmpl.axis2(), nullptr);
EXPECT_EQ(tmpl.axis3(), nullptr);
}
TEST(TableTemplateTest, SetAxes) {
TableTemplate tmpl("tmpl_set");
FloatSeq vals;
vals.push_back(0.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(vals));
tmpl.setAxis1(axis);
EXPECT_NE(tmpl.axis1(), nullptr);
tmpl.setAxis2(axis);
EXPECT_NE(tmpl.axis2(), nullptr);
tmpl.setAxis3(axis);
EXPECT_NE(tmpl.axis3(), nullptr);
}
////////////////////////////////////////////////////////////////
// portLibertyToSta and pwrGndType tests
////////////////////////////////////////////////////////////////
TEST(LibertyUtilTest, PortLibertyToSta) {
std::string result = portLibertyToSta("simple_port");
EXPECT_EQ(result, "simple_port");
}
TEST(LibertyUtilTest, PwrGndTypeName) {
const char *name = pwrGndTypeName(PwrGndType::primary_power);
EXPECT_NE(name, nullptr);
}
TEST(LibertyUtilTest, FindPwrGndType) {
PwrGndType type = findPwrGndType("primary_power");
EXPECT_EQ(type, PwrGndType::primary_power);
}
////////////////////////////////////////////////////////////////
// ScaleFactorPvt name/find tests
////////////////////////////////////////////////////////////////
TEST(ScaleFactorPvtTest, FindByName) {
EXPECT_EQ(findScaleFactorPvt("process"), ScaleFactorPvt::process);
EXPECT_EQ(findScaleFactorPvt("volt"), ScaleFactorPvt::volt);
EXPECT_EQ(findScaleFactorPvt("temp"), ScaleFactorPvt::temp);
EXPECT_EQ(findScaleFactorPvt("nonexistent"), ScaleFactorPvt::unknown);
}
TEST(ScaleFactorPvtTest, PvtToName) {
EXPECT_STREQ(scaleFactorPvtName(ScaleFactorPvt::process), "process");
EXPECT_STREQ(scaleFactorPvtName(ScaleFactorPvt::volt), "volt");
EXPECT_STREQ(scaleFactorPvtName(ScaleFactorPvt::temp), "temp");
}
////////////////////////////////////////////////////////////////
// ScaleFactorType name/find/suffix tests
////////////////////////////////////////////////////////////////
TEST(ScaleFactorTypeTest, FindByName) {
EXPECT_EQ(findScaleFactorType("pin_cap"), ScaleFactorType::pin_cap);
EXPECT_EQ(findScaleFactorType("wire_res"), ScaleFactorType::wire_res);
EXPECT_EQ(findScaleFactorType("wire_cap"), ScaleFactorType::wire_cap);
EXPECT_EQ(findScaleFactorType("min_period"), ScaleFactorType::min_period);
EXPECT_EQ(findScaleFactorType("cell"), ScaleFactorType::cell);
EXPECT_EQ(findScaleFactorType("hold"), ScaleFactorType::hold);
EXPECT_EQ(findScaleFactorType("setup"), ScaleFactorType::setup);
EXPECT_EQ(findScaleFactorType("recovery"), ScaleFactorType::recovery);
EXPECT_EQ(findScaleFactorType("removal"), ScaleFactorType::removal);
EXPECT_EQ(findScaleFactorType("nochange"), ScaleFactorType::nochange);
EXPECT_EQ(findScaleFactorType("skew"), ScaleFactorType::skew);
EXPECT_EQ(findScaleFactorType("leakage_power"), ScaleFactorType::leakage_power);
EXPECT_EQ(findScaleFactorType("internal_power"), ScaleFactorType::internal_power);
EXPECT_EQ(findScaleFactorType("transition"), ScaleFactorType::transition);
EXPECT_EQ(findScaleFactorType("min_pulse_width"), ScaleFactorType::min_pulse_width);
EXPECT_EQ(findScaleFactorType("nonexistent"), ScaleFactorType::unknown);
}
TEST(ScaleFactorTypeTest, TypeToName) {
EXPECT_STREQ(scaleFactorTypeName(ScaleFactorType::pin_cap), "pin_cap");
EXPECT_STREQ(scaleFactorTypeName(ScaleFactorType::wire_cap), "wire_cap");
EXPECT_STREQ(scaleFactorTypeName(ScaleFactorType::wire_res), "wire_res");
EXPECT_STREQ(scaleFactorTypeName(ScaleFactorType::cell), "cell");
EXPECT_STREQ(scaleFactorTypeName(ScaleFactorType::hold), "hold");
EXPECT_STREQ(scaleFactorTypeName(ScaleFactorType::setup), "setup");
EXPECT_STREQ(scaleFactorTypeName(ScaleFactorType::recovery), "recovery");
EXPECT_STREQ(scaleFactorTypeName(ScaleFactorType::removal), "removal");
EXPECT_STREQ(scaleFactorTypeName(ScaleFactorType::transition), "transition");
EXPECT_STREQ(scaleFactorTypeName(ScaleFactorType::min_pulse_width), "min_pulse_width");
}
TEST(ScaleFactorTypeTest, RiseFallSuffix) {
EXPECT_TRUE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::cell));
EXPECT_TRUE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::hold));
EXPECT_TRUE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::setup));
EXPECT_TRUE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::recovery));
EXPECT_TRUE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::removal));
EXPECT_TRUE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::nochange));
EXPECT_TRUE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::skew));
EXPECT_FALSE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::pin_cap));
EXPECT_FALSE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::wire_cap));
EXPECT_FALSE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::transition));
EXPECT_FALSE(scaleFactorTypeRiseFallSuffix(ScaleFactorType::min_pulse_width));
}
TEST(ScaleFactorTypeTest, RiseFallPrefix) {
EXPECT_TRUE(scaleFactorTypeRiseFallPrefix(ScaleFactorType::transition));
EXPECT_FALSE(scaleFactorTypeRiseFallPrefix(ScaleFactorType::cell));
EXPECT_FALSE(scaleFactorTypeRiseFallPrefix(ScaleFactorType::hold));
EXPECT_FALSE(scaleFactorTypeRiseFallPrefix(ScaleFactorType::pin_cap));
EXPECT_FALSE(scaleFactorTypeRiseFallPrefix(ScaleFactorType::min_pulse_width));
}
TEST(ScaleFactorTypeTest, LowHighSuffix) {
EXPECT_TRUE(scaleFactorTypeLowHighSuffix(ScaleFactorType::min_pulse_width));
EXPECT_FALSE(scaleFactorTypeLowHighSuffix(ScaleFactorType::cell));
EXPECT_FALSE(scaleFactorTypeLowHighSuffix(ScaleFactorType::transition));
EXPECT_FALSE(scaleFactorTypeLowHighSuffix(ScaleFactorType::pin_cap));
}
////////////////////////////////////////////////////////////////
// Pvt class tests
////////////////////////////////////////////////////////////////
TEST(PvtTest, Constructor) {
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);
}
TEST(PvtTest, Setters) {
Pvt pvt(1.0f, 1.0f, 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 class tests
////////////////////////////////////////////////////////////////
TEST(OperatingConditionsTest, NameOnlyConstructor) {
OperatingConditions opcond("typical");
EXPECT_STREQ(opcond.name(), "typical");
}
TEST(OperatingConditionsTest, FullConstructor) {
OperatingConditions opcond("worst", 1.0f, 0.9f, 125.0f,
WireloadTree::worst_case);
EXPECT_STREQ(opcond.name(), "worst");
EXPECT_FLOAT_EQ(opcond.process(), 1.0f);
EXPECT_FLOAT_EQ(opcond.voltage(), 0.9f);
EXPECT_FLOAT_EQ(opcond.temperature(), 125.0f);
EXPECT_EQ(opcond.wireloadTree(), WireloadTree::worst_case);
}
TEST(OperatingConditionsTest, SetWireloadTree) {
OperatingConditions opcond("typ");
opcond.setWireloadTree(WireloadTree::balanced);
EXPECT_EQ(opcond.wireloadTree(), WireloadTree::balanced);
}
////////////////////////////////////////////////////////////////
// LibertyLibrary OperatingConditions tests
////////////////////////////////////////////////////////////////
TEST(LibertyLibraryTest, MakeAndFindOperatingConditions) {
LibertyLibrary lib("test_lib", "test.lib");
OperatingConditions *opcond = lib.makeOperatingConditions("typical");
EXPECT_NE(opcond, nullptr);
OperatingConditions *found = lib.findOperatingConditions("typical");
EXPECT_EQ(found, opcond);
EXPECT_EQ(lib.findOperatingConditions("nonexistent"), nullptr);
}
TEST(LibertyLibraryTest, DefaultOperatingConditions) {
LibertyLibrary lib("test_lib", "test.lib");
EXPECT_EQ(lib.defaultOperatingConditions(), nullptr);
OperatingConditions *opcond = lib.makeOperatingConditions("typical");
lib.setDefaultOperatingConditions(opcond);
EXPECT_EQ(lib.defaultOperatingConditions(), opcond);
}
////////////////////////////////////////////////////////////////
// LibertyLibrary scale factor with cell and pvt
////////////////////////////////////////////////////////////////
TEST(LibertyLibraryTest, ScaleFactorWithCell) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
float sf = lib.scaleFactor(ScaleFactorType::cell, &cell, nullptr);
EXPECT_FLOAT_EQ(sf, 1.0f);
}
TEST(LibertyLibraryTest, ScaleFactorWithCellAndRf) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
float sf = lib.scaleFactor(ScaleFactorType::cell, RiseFall::riseIndex(),
&cell, nullptr);
EXPECT_FLOAT_EQ(sf, 1.0f);
}
TEST(LibertyLibraryTest, BuffersAndInverters) {
LibertyLibrary lib("test_lib", "test.lib");
LibertyCellSeq *bufs = lib.buffers();
EXPECT_NE(bufs, nullptr);
// Empty library should have no buffers
EXPECT_EQ(bufs->size(), 0u);
LibertyCellSeq *invs = lib.inverters();
EXPECT_NE(invs, nullptr);
EXPECT_EQ(invs->size(), 0u);
}
TEST(LibertyLibraryTest, FindLibertyCell) {
LibertyLibrary lib("test_lib", "test.lib");
EXPECT_EQ(lib.findLibertyCell("nonexistent"), nullptr);
}
TEST(LibertyLibraryTest, BusDcls) {
LibertyLibrary lib("test_lib", "test.lib");
lib.makeBusDcl("d_bus", 7, 0);
auto dcls = lib.busDcls();
EXPECT_GE(dcls.size(), 1u);
}
////////////////////////////////////////////////////////////////
// BusDcl tests
////////////////////////////////////////////////////////////////
TEST(BusDclTest, Properties) {
BusDcl dcl("data_bus", 15, 0);
EXPECT_EQ(dcl.name(), "data_bus");
EXPECT_EQ(dcl.from(), 15);
EXPECT_EQ(dcl.to(), 0);
}
////////////////////////////////////////////////////////////////
// ModeValueDef tests (via ModeDef)
////////////////////////////////////////////////////////////////
TEST(ModeDefTest, DefineAndFindValue) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
ModeDef *mode = cell.makeModeDef("scan_mode");
EXPECT_NE(mode, nullptr);
FuncExpr *cond = FuncExpr::makeOne();
ModeValueDef *valdef = mode->defineValue("test_value", cond, "A==1");
EXPECT_NE(valdef, nullptr);
EXPECT_EQ(valdef->value(), "test_value");
EXPECT_EQ(valdef->cond(), cond);
EXPECT_EQ(valdef->sdfCond(), "A==1");
const ModeValueDef *found = mode->findValueDef("test_value");
EXPECT_EQ(found, valdef);
EXPECT_EQ(mode->findValueDef("nonexistent"), nullptr);
const ModeValueMap *vals = mode->values();
EXPECT_NE(vals, nullptr);
}
////////////////////////////////////////////////////////////////
// LibertyCell additional getters
////////////////////////////////////////////////////////////////
// isDisabledConstraint / setIsDisabledConstraint removed in MCMM update
TEST(TestCellTest, HasInferedRegTimingArcs) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
EXPECT_FALSE(cell.hasInferedRegTimingArcs());
cell.setHasInferedRegTimingArcs(true);
EXPECT_TRUE(cell.hasInferedRegTimingArcs());
}
TEST(TestCellTest, HasSequentials) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
EXPECT_FALSE(cell.hasSequentials());
}
TEST(TestCellTest, SequentialsEmpty) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
auto &seqs = cell.sequentials();
EXPECT_EQ(seqs.size(), 0u);
}
TEST(TestCellTest, TestCellPtr) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
EXPECT_EQ(cell.testCell(), nullptr);
}
TEST(TestCellTest, LeakagePowerExists) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
EXPECT_FALSE(cell.leakagePowerExists());
cell.setLeakagePower(0.005f);
EXPECT_TRUE(cell.leakagePowerExists());
}
TEST(TestCellTest, InternalPowersEmpty) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
auto &powers = cell.internalPowers();
EXPECT_EQ(powers.size(), 0u);
}
TEST(TestCellTest, LeakagePowersEmpty) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
const auto &leak_powers = cell.leakagePowers();
EXPECT_EQ(leak_powers.size(), 0u);
}
TEST(TestCellTest, StatetableNull) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
EXPECT_EQ(cell.statetable(), nullptr);
}
TEST(TestCellTest, TimingArcSetsEmpty) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
auto &arcsets = cell.timingArcSets();
EXPECT_EQ(arcsets.size(), 0u);
}
TEST(TestCellTest, FootprintDefault) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
const char *fp = cell.footprint();
// Empty string or nullptr for default
if (fp)
EXPECT_EQ(fp, "");
}
TEST(TestCellTest, SetFootprint) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
cell.setFootprint("INV_FP");
EXPECT_STREQ(cell.footprint(), "INV_FP");
}
TEST(TestCellTest, UserFunctionClassDefault) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
const char *ufc = cell.userFunctionClass();
if (ufc)
EXPECT_EQ(ufc, "");
}
TEST(TestCellTest, SetUserFunctionClass) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
cell.setUserFunctionClass("inverter");
EXPECT_STREQ(cell.userFunctionClass(), "inverter");
}
TEST(TestCellTest, SwitchCellTypeGetter) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
cell.setSwitchCellType(SwitchCellType::fine_grain);
EXPECT_EQ(cell.switchCellType(), SwitchCellType::fine_grain);
}
TEST(TestCellTest, LevelShifterTypeGetter) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
cell.setLevelShifterType(LevelShifterType::LH);
EXPECT_EQ(cell.levelShifterType(), LevelShifterType::LH);
cell.setLevelShifterType(LevelShifterType::HL_LH);
EXPECT_EQ(cell.levelShifterType(), LevelShifterType::HL_LH);
}
TEST(TestCellTest, IsClockCellGetter) {
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib");
EXPECT_FALSE(cell.isClockCell());
cell.setIsClockCell(true);
EXPECT_TRUE(cell.isClockCell());
}
// Note: timingTypeString is defined in TimingArc.cc but not declared
// in a public header, so we cannot test it directly here.
////////////////////////////////////////////////////////////////
// FindTimingType additional values
////////////////////////////////////////////////////////////////
TEST(TimingTypeTest, FindTimingTypeAdditional) {
EXPECT_EQ(findTimingType("combinational_rise"), TimingType::combinational_rise);
EXPECT_EQ(findTimingType("combinational_fall"), TimingType::combinational_fall);
EXPECT_EQ(findTimingType("recovery_falling"), TimingType::recovery_falling);
EXPECT_EQ(findTimingType("removal_rising"), TimingType::removal_rising);
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("three_state_disable_rise"), TimingType::three_state_disable_rise);
EXPECT_EQ(findTimingType("three_state_disable_fall"), TimingType::three_state_disable_fall);
EXPECT_EQ(findTimingType("skew_rising"), TimingType::skew_rising);
EXPECT_EQ(findTimingType("skew_falling"), TimingType::skew_falling);
EXPECT_EQ(findTimingType("nochange_high_high"), TimingType::nochange_high_high);
EXPECT_EQ(findTimingType("nochange_high_low"), TimingType::nochange_high_low);
EXPECT_EQ(findTimingType("nochange_low_high"), TimingType::nochange_low_high);
EXPECT_EQ(findTimingType("nochange_low_low"), TimingType::nochange_low_low);
EXPECT_EQ(findTimingType("non_seq_setup_falling"), TimingType::non_seq_setup_falling);
EXPECT_EQ(findTimingType("non_seq_setup_rising"), TimingType::non_seq_setup_rising);
EXPECT_EQ(findTimingType("non_seq_hold_falling"), TimingType::non_seq_hold_falling);
EXPECT_EQ(findTimingType("non_seq_hold_rising"), TimingType::non_seq_hold_rising);
EXPECT_EQ(findTimingType("retaining_time"), TimingType::retaining_time);
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 additional coverage
////////////////////////////////////////////////////////////////
TEST(TimingTypeTest, ScaleFactorTypeAdditional) {
EXPECT_EQ(timingTypeScaleFactorType(TimingType::recovery_falling),
ScaleFactorType::recovery);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::removal_rising),
ScaleFactorType::removal);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::skew_falling),
ScaleFactorType::skew);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::nochange_high_low),
ScaleFactorType::nochange);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::nochange_low_high),
ScaleFactorType::nochange);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::nochange_low_low),
ScaleFactorType::nochange);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::non_seq_setup_falling),
ScaleFactorType::setup);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::non_seq_setup_rising),
ScaleFactorType::setup);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::non_seq_hold_falling),
ScaleFactorType::hold);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::non_seq_hold_rising),
ScaleFactorType::hold);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::retaining_time),
ScaleFactorType::cell);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::rising_edge),
ScaleFactorType::cell);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::falling_edge),
ScaleFactorType::cell);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::clear),
ScaleFactorType::cell);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::preset),
ScaleFactorType::cell);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::three_state_enable),
ScaleFactorType::cell);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::three_state_disable),
ScaleFactorType::cell);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::three_state_enable_rise),
ScaleFactorType::cell);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::three_state_enable_fall),
ScaleFactorType::cell);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::three_state_disable_rise),
ScaleFactorType::cell);
EXPECT_EQ(timingTypeScaleFactorType(TimingType::three_state_disable_fall),
ScaleFactorType::cell);
}
////////////////////////////////////////////////////////////////
// ScanSignalType full coverage
////////////////////////////////////////////////////////////////
TEST(ScanSignalTypeTest, AllNames) {
EXPECT_NE(scanSignalTypeName(ScanSignalType::enable), nullptr);
EXPECT_NE(scanSignalTypeName(ScanSignalType::enable_inverted), nullptr);
EXPECT_NE(scanSignalTypeName(ScanSignalType::clock), nullptr);
EXPECT_NE(scanSignalTypeName(ScanSignalType::clock_a), nullptr);
EXPECT_NE(scanSignalTypeName(ScanSignalType::clock_b), nullptr);
EXPECT_NE(scanSignalTypeName(ScanSignalType::input), nullptr);
EXPECT_NE(scanSignalTypeName(ScanSignalType::input_inverted), nullptr);
EXPECT_NE(scanSignalTypeName(ScanSignalType::output), nullptr);
EXPECT_NE(scanSignalTypeName(ScanSignalType::output_inverted), nullptr);
}
////////////////////////////////////////////////////////////////
// PwrGndType full coverage
////////////////////////////////////////////////////////////////
TEST(LibertyUtilTest, PwrGndTypeAllNames) {
EXPECT_NE(pwrGndTypeName(PwrGndType::primary_power), nullptr);
EXPECT_NE(pwrGndTypeName(PwrGndType::primary_ground), nullptr);
EXPECT_NE(pwrGndTypeName(PwrGndType::backup_power), nullptr);
EXPECT_NE(pwrGndTypeName(PwrGndType::backup_ground), nullptr);
EXPECT_NE(pwrGndTypeName(PwrGndType::internal_power), nullptr);
EXPECT_NE(pwrGndTypeName(PwrGndType::internal_ground), nullptr);
EXPECT_NE(pwrGndTypeName(PwrGndType::nwell), nullptr);
EXPECT_NE(pwrGndTypeName(PwrGndType::pwell), nullptr);
EXPECT_NE(pwrGndTypeName(PwrGndType::deepnwell), nullptr);
EXPECT_NE(pwrGndTypeName(PwrGndType::deeppwell), nullptr);
}
TEST(LibertyUtilTest, FindPwrGndTypeAll) {
EXPECT_EQ(findPwrGndType("primary_ground"), PwrGndType::primary_ground);
EXPECT_EQ(findPwrGndType("backup_power"), PwrGndType::backup_power);
EXPECT_EQ(findPwrGndType("backup_ground"), PwrGndType::backup_ground);
EXPECT_EQ(findPwrGndType("internal_power"), PwrGndType::internal_power);
EXPECT_EQ(findPwrGndType("internal_ground"), PwrGndType::internal_ground);
EXPECT_EQ(findPwrGndType("nwell"), PwrGndType::nwell);
EXPECT_EQ(findPwrGndType("pwell"), PwrGndType::pwell);
EXPECT_EQ(findPwrGndType("deepnwell"), PwrGndType::deepnwell);
EXPECT_EQ(findPwrGndType("deeppwell"), PwrGndType::deeppwell);
EXPECT_EQ(findPwrGndType("nonexistent"), PwrGndType::none);
}
TEST(LibertyUtilTest, PortLibertyToStaWithBrackets) {
std::string result = portLibertyToSta("bus[0]");
// Should convert liberty port name to Sta format
EXPECT_FALSE(result.empty());
}
////////////////////////////////////////////////////////////////
// InternalPowerModel tests
////////////////////////////////////////////////////////////////
TEST(InternalPowerModelTest, PowerLookupOrder0) {
TablePtr tbl = std::make_shared<Table>(5.0f);
TableModel *table_model = new TableModel(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
InternalPowerModel model(table_model);
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "INV", "test.lib");
float pwr = model.power(&cell, nullptr, 0.5f, 1.0f);
EXPECT_FLOAT_EQ(pwr, 5.0f);
}
TEST(InternalPowerModelTest, ReportPowerOrder0) {
TablePtr tbl = std::make_shared<Table>(3.0f);
TableModel *table_model = new TableModel(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
InternalPowerModel model(table_model);
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "INV", "test.lib");
std::string report = model.reportPower(&cell, nullptr, 0.5f, 1.0f, 3);
EXPECT_FALSE(report.empty());
}
TEST(InternalPowerModelTest, PowerLookupOrder1) {
FloatSeq axis_values;
axis_values.push_back(0.0f);
axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(axis_values));
FloatSeq values;
values.push_back(1.0f);
values.push_back(3.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel *table_model = new TableModel(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
InternalPowerModel model(table_model);
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "INV", "test.lib");
float pwr = model.power(&cell, nullptr, 0.5f, 0.0f);
EXPECT_GT(pwr, 0.0f);
}
TEST(InternalPowerModelTest, PowerLookupOrder2) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.0f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.0f); ax2_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(ax2_vals));
FloatTable values;
FloatSeq row0; row0.push_back(1.0f); row0.push_back(2.0f);
FloatSeq row1; row1.push_back(3.0f); row1.push_back(4.0f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
TablePtr tbl = std::make_shared<Table>(std::move(values), axis1, axis2);
TableModel *table_model = new TableModel(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
InternalPowerModel model(table_model);
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "INV", "test.lib");
float pwr = model.power(&cell, nullptr, 0.5f, 0.5f);
EXPECT_GT(pwr, 0.0f);
}
////////////////////////////////////////////////////////////////
// GateTableModel additional tests
////////////////////////////////////////////////////////////////
TEST(GateTableModelTest, CheckAxesOrder1BadAxis) {
FloatSeq axis_values;
axis_values.push_back(0.1f); axis_values.push_back(1.0f);
// path_depth is not a valid gate model axis
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::path_depth, std::move(axis_values));
FloatSeq values;
values.push_back(1.0f); values.push_back(2.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_FALSE(GateTableModel::checkAxes(&tbl_model));
}
TEST(GateTableModelTest, CheckAxesOrder2BadAxis) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.1f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.1f); ax2_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::path_depth, std::move(ax2_vals));
FloatTable values;
FloatSeq row0; row0.push_back(1.0f); row0.push_back(2.0f);
FloatSeq row1; row1.push_back(3.0f); row1.push_back(4.0f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
TablePtr tbl = std::make_shared<Table>(std::move(values), axis1, axis2);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_FALSE(GateTableModel::checkAxes(&tbl_model));
}
////////////////////////////////////////////////////////////////
// CheckTableModel tests
////////////////////////////////////////////////////////////////
TEST(CheckTableModelTest, CheckAxesOrder0) {
TablePtr tbl = std::make_shared<Table>(1.0f);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_TRUE(CheckTableModel::checkAxes(&tbl_model));
}
TEST(CheckTableModelTest, CheckAxesOrder1) {
FloatSeq axis_values;
axis_values.push_back(0.1f); axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::related_pin_transition, std::move(axis_values));
FloatSeq values;
values.push_back(1.0f); values.push_back(2.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_TRUE(CheckTableModel::checkAxes(&tbl_model));
}
TEST(CheckTableModelTest, CheckAxesOrder1BadAxis) {
FloatSeq axis_values;
axis_values.push_back(0.1f); axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::path_depth, std::move(axis_values));
FloatSeq values;
values.push_back(1.0f); values.push_back(2.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_FALSE(CheckTableModel::checkAxes(&tbl_model));
}
////////////////////////////////////////////////////////////////
// ReceiverModel checkAxes
////////////////////////////////////////////////////////////////
TEST(ReceiverModelTest, CheckAxesOrder0False) {
// Table0 has no axes, ReceiverModel requires input_net_transition axis
TablePtr tbl = std::make_shared<Table>(1.0f);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_FALSE(ReceiverModel::checkAxes(&tbl_model));
}
TEST(ReceiverModelTest, CheckAxesOrder1Valid) {
FloatSeq axis_values;
axis_values.push_back(0.1f); axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::input_net_transition, std::move(axis_values));
FloatSeq values;
values.push_back(1.0f); values.push_back(2.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_TRUE(ReceiverModel::checkAxes(&tbl_model));
}
TEST(ReceiverModelTest, CheckAxesOrder1BadAxis) {
FloatSeq axis_values;
axis_values.push_back(0.1f); axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::path_depth, std::move(axis_values));
FloatSeq values;
values.push_back(1.0f); values.push_back(2.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_FALSE(ReceiverModel::checkAxes(&tbl_model));
}
////////////////////////////////////////////////////////////////
// LibertyLibrary checkSlewDegradationAxes bad axis
////////////////////////////////////////////////////////////////
TEST(LibertyLibraryTest, CheckSlewDegradationAxesBadAxis) {
FloatSeq axis_values;
axis_values.push_back(0.1f); axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::path_depth, std::move(axis_values));
FloatSeq values;
values.push_back(0.1f); values.push_back(1.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel tbl_model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
EXPECT_FALSE(LibertyLibrary::checkSlewDegradationAxes(&tbl_model));
}
////////////////////////////////////////////////////////////////
// Table report methods (Table0, Table1 report via Report*)
// Covers Table::report virtual functions
////////////////////////////////////////////////////////////////
TEST(Table0Test, ReportValue) {
Table tbl(42.0f);
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "INV", "test.lib");
const Units *units = lib.units();
std::string report = tbl.reportValue("Power", &cell, nullptr,
0.0f, nullptr, 0.0f, 0.0f,
units->powerUnit(), 3);
EXPECT_FALSE(report.empty());
}
////////////////////////////////////////////////////////////////
// TableModel with Pvt scaling
////////////////////////////////////////////////////////////////
TEST(TableModelTest, FindValueWithPvtScaling) {
TablePtr tbl = std::make_shared<Table>(10.0f);
TableModel model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "INV", "test.lib");
// Without pvt, scale factor should be 1.0
float result = model.findValue(&cell, nullptr, 0.0f, 0.0f, 0.0f);
EXPECT_FLOAT_EQ(result, 10.0f);
}
TEST(TableModelTest, SetScaleFactorType) {
TablePtr tbl = std::make_shared<Table>(10.0f);
TableModel model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
model.setScaleFactorType(ScaleFactorType::hold);
// Just verify it doesn't crash
EXPECT_EQ(model.order(), 0);
}
TEST(TableModelTest, SetIsScaled) {
TablePtr tbl = std::make_shared<Table>(10.0f);
TableModel model(tbl, nullptr, ScaleFactorType::cell, RiseFall::rise());
model.setIsScaled(true);
// Verify it doesn't crash
EXPECT_EQ(model.order(), 0);
}
////////////////////////////////////////////////////////////////
// Table1 findValue with extrapolation info
////////////////////////////////////////////////////////////////
TEST(Table1ExtraTest, FindValueWithExtrapolation) {
FloatSeq axis_values;
axis_values.push_back(0.0f);
axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::input_net_transition, std::move(axis_values));
FloatSeq values;
values.push_back(10.0f);
values.push_back(20.0f);
Table tbl(std::move(values), axis);
// In bounds - single arg findValue
float result_in = tbl.findValue(0.5f);
EXPECT_NEAR(result_in, 15.0f, 0.01f);
// Out of bounds (above) - extrapolation
float result_above = tbl.findValue(2.0f);
EXPECT_NEAR(result_above, 30.0f, 0.01f);
// Out of bounds (below) - extrapolation
float result_below = tbl.findValue(-1.0f);
EXPECT_NEAR(result_below, 0.0f, 1.0f);
// findValueClip - clips to bounds
float clip_above = tbl.findValueClip(2.0f);
EXPECT_FLOAT_EQ(clip_above, 20.0f);
float clip_below = tbl.findValueClip(-1.0f);
EXPECT_FLOAT_EQ(clip_below, 0.0f);
}
TEST(Table1ExtraTest, ValuesPointer) {
FloatSeq axis_values;
axis_values.push_back(0.0f); axis_values.push_back(1.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::input_net_transition, std::move(axis_values));
FloatSeq vals;
vals.push_back(10.0f); vals.push_back(20.0f);
Table tbl(std::move(vals), axis);
FloatSeq *v = tbl.values();
EXPECT_NE(v, nullptr);
EXPECT_EQ(v->size(), 2u);
}
TEST(Table1ExtraTest, Axis1ptr) {
FloatSeq axis_values;
axis_values.push_back(0.0f);
auto axis = std::make_shared<TableAxis>(
TableAxisVariable::input_net_transition, std::move(axis_values));
FloatSeq vals;
vals.push_back(10.0f);
Table tbl(std::move(vals), axis);
auto aptr = tbl.axis1ptr();
EXPECT_NE(aptr, nullptr);
}
////////////////////////////////////////////////////////////////
// Table2 values3 and specific value access
////////////////////////////////////////////////////////////////
TEST(Table2Test, Values3Pointer) {
FloatSeq ax1_vals;
ax1_vals.push_back(0.0f); ax1_vals.push_back(1.0f);
FloatSeq ax2_vals;
ax2_vals.push_back(0.0f); ax2_vals.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_net_transition, std::move(ax1_vals));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(ax2_vals));
FloatTable values;
FloatSeq row0; row0.push_back(1.0f); row0.push_back(2.0f);
FloatSeq row1; row1.push_back(3.0f); row1.push_back(4.0f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
Table tbl(std::move(values), axis1, axis2);
FloatTable *v3 = tbl.values3();
EXPECT_NE(v3, nullptr);
EXPECT_EQ(v3->size(), 2u);
}
////////////////////////////////////////////////////////////////
// TableAxis values() pointer test
////////////////////////////////////////////////////////////////
TEST(TableAxisExtraTest, ValuesReference) {
FloatSeq vals;
vals.push_back(1.0f); vals.push_back(2.0f);
TableAxis axis(TableAxisVariable::input_net_transition, std::move(vals));
const FloatSeq &v = axis.values();
EXPECT_EQ(v.size(), 2u);
}
////////////////////////////////////////////////////////////////
// TableTemplate name setter test
////////////////////////////////////////////////////////////////
TEST(TableTemplateTest, SetName) {
TableTemplate tmpl("original_name");
EXPECT_EQ(tmpl.name(), "original_name");
tmpl.setName("new_name");
EXPECT_EQ(tmpl.name(), "new_name");
}
TEST(TableTemplateTest, AxisPtrs) {
FloatSeq vals1;
vals1.push_back(0.1f); vals1.push_back(1.0f);
FloatSeq vals2;
vals2.push_back(0.01f); vals2.push_back(0.1f);
FloatSeq vals3;
vals3.push_back(0.0f); vals3.push_back(1.0f);
auto axis1 = std::make_shared<TableAxis>(
TableAxisVariable::input_transition_time, std::move(vals1));
auto axis2 = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance, std::move(vals2));
auto axis3 = std::make_shared<TableAxis>(
TableAxisVariable::related_pin_transition, std::move(vals3));
TableTemplate tmpl("tmpl_3d", axis1, axis2, axis3);
EXPECT_NE(tmpl.axis1ptr(), nullptr);
EXPECT_NE(tmpl.axis2ptr(), nullptr);
EXPECT_NE(tmpl.axis3ptr(), nullptr);
}
////////////////////////////////////////////////////////////////
// LibertyLibrary DriverWaveform
////////////////////////////////////////////////////////////////
TEST(LibertyLibraryTest, DriverWaveformDefault) {
LibertyLibrary lib("test_lib", "test.lib");
// No driver waveforms added -> default is nullptr
EXPECT_EQ(lib.driverWaveformDefault(), nullptr);
EXPECT_EQ(lib.findDriverWaveform("nonexistent"), nullptr);
}
} // namespace sta