OpenSTA/power/test/cpp/TestPower.cc

1498 lines
50 KiB
C++

#include <gtest/gtest.h>
#include "MinMax.hh"
#include "Transition.hh"
#include "StringUtil.hh"
// Power module unit tests
// Power module smoke tests
namespace sta {
class PowerSmokeTest : public ::testing::Test {};
TEST_F(PowerSmokeTest, TransitionsForPower) {
// Power calculation uses rise/fall transitions
EXPECT_NE(RiseFall::rise(), nullptr);
EXPECT_NE(RiseFall::fall(), nullptr);
EXPECT_EQ(RiseFall::range().size(), 2u);
}
TEST_F(PowerSmokeTest, MinMaxForPower) {
// Power uses min/max for different analysis corners
EXPECT_NE(MinMax::min(), nullptr);
EXPECT_NE(MinMax::max(), nullptr);
}
TEST_F(PowerSmokeTest, StringUtils) {
// VCD reader uses string utilities
EXPECT_TRUE(isDigits("12345"));
EXPECT_FALSE(isDigits("abc"));
EXPECT_FALSE(isDigits("12a34"));
}
////////////////////////////////////////////////////////////////
// PowerResult tests
} // namespace sta
#include "NetworkClass.hh"
#include "PowerClass.hh"
namespace sta {
class PowerResultTest : public ::testing::Test {};
TEST_F(PowerResultTest, DefaultConstruction) {
PowerResult result;
EXPECT_FLOAT_EQ(result.internal(), 0.0f);
EXPECT_FLOAT_EQ(result.switching(), 0.0f);
EXPECT_FLOAT_EQ(result.leakage(), 0.0f);
EXPECT_FLOAT_EQ(result.total(), 0.0f);
}
TEST_F(PowerResultTest, IncrInternal) {
PowerResult result;
result.incrInternal(1.0e-3f);
EXPECT_FLOAT_EQ(result.internal(), 1.0e-3f);
result.incrInternal(2.0e-3f);
EXPECT_FLOAT_EQ(result.internal(), 3.0e-3f);
}
TEST_F(PowerResultTest, IncrSwitching) {
PowerResult result;
result.incrSwitching(5.0e-4f);
EXPECT_FLOAT_EQ(result.switching(), 5.0e-4f);
result.incrSwitching(3.0e-4f);
EXPECT_FLOAT_EQ(result.switching(), 8.0e-4f);
}
TEST_F(PowerResultTest, IncrLeakage) {
PowerResult result;
result.incrLeakage(1.0e-6f);
EXPECT_FLOAT_EQ(result.leakage(), 1.0e-6f);
result.incrLeakage(2.0e-6f);
EXPECT_FLOAT_EQ(result.leakage(), 3.0e-6f);
}
TEST_F(PowerResultTest, Total) {
PowerResult result;
result.incrInternal(1.0e-3f);
result.incrSwitching(2.0e-3f);
result.incrLeakage(3.0e-3f);
EXPECT_FLOAT_EQ(result.total(), 6.0e-3f);
}
TEST_F(PowerResultTest, Clear) {
PowerResult result;
result.incrInternal(1.0e-3f);
result.incrSwitching(2.0e-3f);
result.incrLeakage(3.0e-3f);
EXPECT_FLOAT_EQ(result.total(), 6.0e-3f);
result.clear();
EXPECT_FLOAT_EQ(result.internal(), 0.0f);
EXPECT_FLOAT_EQ(result.switching(), 0.0f);
EXPECT_FLOAT_EQ(result.leakage(), 0.0f);
EXPECT_FLOAT_EQ(result.total(), 0.0f);
}
TEST_F(PowerResultTest, Incr) {
PowerResult result1;
result1.incrInternal(1.0e-3f);
result1.incrSwitching(2.0e-3f);
result1.incrLeakage(3.0e-3f);
PowerResult result2;
result2.incrInternal(4.0e-3f);
result2.incrSwitching(5.0e-3f);
result2.incrLeakage(6.0e-3f);
result1.incr(result2);
EXPECT_FLOAT_EQ(result1.internal(), 5.0e-3f);
EXPECT_FLOAT_EQ(result1.switching(), 7.0e-3f);
EXPECT_FLOAT_EQ(result1.leakage(), 9.0e-3f);
EXPECT_FLOAT_EQ(result1.total(), 21.0e-3f);
}
TEST_F(PowerResultTest, IncrSelf) {
PowerResult result;
result.incrInternal(1.0e-3f);
result.incrSwitching(2.0e-3f);
result.incrLeakage(3.0e-3f);
result.incr(result);
EXPECT_FLOAT_EQ(result.internal(), 2.0e-3f);
EXPECT_FLOAT_EQ(result.switching(), 4.0e-3f);
EXPECT_FLOAT_EQ(result.leakage(), 6.0e-3f);
}
TEST_F(PowerResultTest, IncrEmptyResult) {
PowerResult result;
result.incrInternal(1.0e-3f);
PowerResult empty;
result.incr(empty);
EXPECT_FLOAT_EQ(result.internal(), 1.0e-3f);
EXPECT_FLOAT_EQ(result.switching(), 0.0f);
EXPECT_FLOAT_EQ(result.leakage(), 0.0f);
}
////////////////////////////////////////////////////////////////
// PwrActivity tests
class PwrActivityTest : public ::testing::Test {};
TEST_F(PwrActivityTest, DefaultConstruction) {
PwrActivity activity;
EXPECT_FLOAT_EQ(activity.density(), 0.0f);
EXPECT_FLOAT_EQ(activity.duty(), 0.0f);
EXPECT_EQ(activity.origin(), PwrActivityOrigin::unknown);
EXPECT_FALSE(activity.isSet());
}
TEST_F(PwrActivityTest, ParameterizedConstruction) {
PwrActivity activity(1000.0f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(activity.density(), 1000.0f);
EXPECT_FLOAT_EQ(activity.duty(), 0.5f);
EXPECT_EQ(activity.origin(), PwrActivityOrigin::user);
EXPECT_TRUE(activity.isSet());
}
TEST_F(PwrActivityTest, SetDensity) {
PwrActivity activity;
activity.setDensity(500.0f);
EXPECT_FLOAT_EQ(activity.density(), 500.0f);
}
TEST_F(PwrActivityTest, SetDuty) {
PwrActivity activity;
activity.setDuty(0.75f);
EXPECT_FLOAT_EQ(activity.duty(), 0.75f);
}
TEST_F(PwrActivityTest, SetOrigin) {
PwrActivity activity;
activity.setOrigin(PwrActivityOrigin::vcd);
EXPECT_EQ(activity.origin(), PwrActivityOrigin::vcd);
EXPECT_TRUE(activity.isSet());
}
TEST_F(PwrActivityTest, Init) {
PwrActivity activity(1000.0f, 0.5f, PwrActivityOrigin::user);
EXPECT_TRUE(activity.isSet());
activity.init();
EXPECT_FLOAT_EQ(activity.density(), 0.0f);
EXPECT_FLOAT_EQ(activity.duty(), 0.0f);
EXPECT_EQ(activity.origin(), PwrActivityOrigin::unknown);
EXPECT_FALSE(activity.isSet());
}
TEST_F(PwrActivityTest, Set) {
PwrActivity activity;
activity.set(2000.0f, 0.3f, PwrActivityOrigin::propagated);
EXPECT_FLOAT_EQ(activity.density(), 2000.0f);
EXPECT_FLOAT_EQ(activity.duty(), 0.3f);
EXPECT_EQ(activity.origin(), PwrActivityOrigin::propagated);
EXPECT_TRUE(activity.isSet());
}
TEST_F(PwrActivityTest, IsSetForAllOrigins) {
PwrActivity activity;
activity.setOrigin(PwrActivityOrigin::unknown);
EXPECT_FALSE(activity.isSet());
activity.setOrigin(PwrActivityOrigin::global);
EXPECT_TRUE(activity.isSet());
activity.setOrigin(PwrActivityOrigin::input);
EXPECT_TRUE(activity.isSet());
activity.setOrigin(PwrActivityOrigin::user);
EXPECT_TRUE(activity.isSet());
activity.setOrigin(PwrActivityOrigin::vcd);
EXPECT_TRUE(activity.isSet());
activity.setOrigin(PwrActivityOrigin::saif);
EXPECT_TRUE(activity.isSet());
activity.setOrigin(PwrActivityOrigin::propagated);
EXPECT_TRUE(activity.isSet());
activity.setOrigin(PwrActivityOrigin::clock);
EXPECT_TRUE(activity.isSet());
activity.setOrigin(PwrActivityOrigin::constant);
EXPECT_TRUE(activity.isSet());
}
TEST_F(PwrActivityTest, OriginName) {
PwrActivity activity;
activity.setOrigin(PwrActivityOrigin::global);
EXPECT_STREQ(activity.originName(), "global");
activity.setOrigin(PwrActivityOrigin::input);
EXPECT_STREQ(activity.originName(), "input");
activity.setOrigin(PwrActivityOrigin::user);
EXPECT_STREQ(activity.originName(), "user");
activity.setOrigin(PwrActivityOrigin::vcd);
EXPECT_STREQ(activity.originName(), "vcd");
activity.setOrigin(PwrActivityOrigin::saif);
EXPECT_STREQ(activity.originName(), "saif");
activity.setOrigin(PwrActivityOrigin::propagated);
EXPECT_STREQ(activity.originName(), "propagated");
activity.setOrigin(PwrActivityOrigin::clock);
EXPECT_STREQ(activity.originName(), "clock");
activity.setOrigin(PwrActivityOrigin::constant);
EXPECT_STREQ(activity.originName(), "constant");
activity.setOrigin(PwrActivityOrigin::unknown);
EXPECT_STREQ(activity.originName(), "unknown");
}
TEST_F(PwrActivityTest, VerySmallDensityClipped) {
// Density smaller than min_density (1E-10) should be clipped to 0
PwrActivity activity(1e-11f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(activity.density(), 0.0f);
}
TEST_F(PwrActivityTest, DensityAboveThresholdNotClipped) {
// Density above min_density should not be clipped
PwrActivity activity(1e-9f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(activity.density(), 1e-9f);
}
TEST_F(PwrActivityTest, SetWithSmallDensity) {
PwrActivity activity;
activity.set(1e-12f, 0.5f, PwrActivityOrigin::propagated);
EXPECT_FLOAT_EQ(activity.density(), 0.0f); // clipped
}
TEST_F(PwrActivityTest, NegativeSmallDensityClipped) {
// Negative density smaller than -min_density should be clipped
PwrActivity activity(-1e-11f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(activity.density(), 0.0f);
}
TEST_F(PwrActivityTest, NormalDensity) {
PwrActivity activity(1e6f, 0.5f, PwrActivityOrigin::vcd);
EXPECT_FLOAT_EQ(activity.density(), 1e6f);
}
TEST_F(PwrActivityTest, ZeroDuty) {
PwrActivity activity(1000.0f, 0.0f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(activity.duty(), 0.0f);
}
TEST_F(PwrActivityTest, FullDuty) {
PwrActivity activity(1000.0f, 1.0f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(activity.duty(), 1.0f);
}
////////////////////////////////////////////////////////////////
// Additional PowerResult tests for edge cases
TEST_F(PowerResultTest, NegativeInternalPower) {
PowerResult result;
result.incrInternal(-1.0e-3f);
EXPECT_FLOAT_EQ(result.internal(), -1.0e-3f);
}
TEST_F(PowerResultTest, MixedSignPower) {
PowerResult result;
result.incrInternal(5.0e-3f);
result.incrSwitching(3.0e-3f);
result.incrLeakage(-1.0e-3f);
EXPECT_FLOAT_EQ(result.total(), 7.0e-3f);
}
TEST_F(PowerResultTest, ClearAndReuse) {
PowerResult result;
result.incrInternal(1.0f);
result.incrSwitching(2.0f);
result.incrLeakage(3.0f);
result.clear();
result.incrInternal(10.0f);
EXPECT_FLOAT_EQ(result.internal(), 10.0f);
EXPECT_FLOAT_EQ(result.switching(), 0.0f);
EXPECT_FLOAT_EQ(result.leakage(), 0.0f);
EXPECT_FLOAT_EQ(result.total(), 10.0f);
}
TEST_F(PowerResultTest, MultipleIncrements) {
PowerResult result;
for (int i = 0; i < 100; i++) {
result.incrInternal(1.0e-6f);
result.incrSwitching(2.0e-6f);
result.incrLeakage(3.0e-6f);
}
EXPECT_NEAR(result.internal(), 100.0e-6f, 1e-9f);
EXPECT_NEAR(result.switching(), 200.0e-6f, 1e-9f);
EXPECT_NEAR(result.leakage(), 300.0e-6f, 1e-9f);
EXPECT_NEAR(result.total(), 600.0e-6f, 1e-9f);
}
////////////////////////////////////////////////////////////////
// Additional PwrActivity origin tests
////////////////////////////////////////////////////////////////
TEST_F(PwrActivityTest, OriginNames) {
// Test all origin name strings
EXPECT_STREQ(PwrActivity(0.0f, 0.0f, PwrActivityOrigin::unknown).originName(), "unknown");
EXPECT_STREQ(PwrActivity(0.0f, 0.0f, PwrActivityOrigin::global).originName(), "global");
EXPECT_STREQ(PwrActivity(0.0f, 0.0f, PwrActivityOrigin::input).originName(), "input");
EXPECT_STREQ(PwrActivity(0.0f, 0.0f, PwrActivityOrigin::user).originName(), "user");
EXPECT_STREQ(PwrActivity(0.0f, 0.0f, PwrActivityOrigin::vcd).originName(), "vcd");
EXPECT_STREQ(PwrActivity(0.0f, 0.0f, PwrActivityOrigin::saif).originName(), "saif");
EXPECT_STREQ(PwrActivity(0.0f, 0.0f, PwrActivityOrigin::propagated).originName(), "propagated");
EXPECT_STREQ(PwrActivity(0.0f, 0.0f, PwrActivityOrigin::clock).originName(), "clock");
EXPECT_STREQ(PwrActivity(0.0f, 0.0f, PwrActivityOrigin::constant).originName(), "constant");
}
// Construct and test with explicit density/duty
TEST_F(PwrActivityTest, ConstructionDetails) {
PwrActivity act(500.0f, 0.25f, PwrActivityOrigin::propagated);
EXPECT_FLOAT_EQ(act.density(), 500.0f);
EXPECT_FLOAT_EQ(act.duty(), 0.25f);
EXPECT_EQ(act.origin(), PwrActivityOrigin::propagated);
}
// Test isSet for various origins
TEST_F(PwrActivityTest, IsSetOriginCombinations) {
PwrActivity a;
a.setOrigin(PwrActivityOrigin::unknown);
EXPECT_FALSE(a.isSet());
for (PwrActivityOrigin origin : {PwrActivityOrigin::global,
PwrActivityOrigin::input,
PwrActivityOrigin::user,
PwrActivityOrigin::vcd,
PwrActivityOrigin::saif,
PwrActivityOrigin::propagated,
PwrActivityOrigin::clock,
PwrActivityOrigin::constant}) {
a.setOrigin(origin);
EXPECT_TRUE(a.isSet());
}
// unknown origin means not set
a.setOrigin(PwrActivityOrigin::unknown);
EXPECT_FALSE(a.isSet());
}
// Test init and then set again
TEST_F(PwrActivityTest, InitThenSet) {
PwrActivity act(100.0f, 0.5f, PwrActivityOrigin::user);
act.init();
EXPECT_FALSE(act.isSet());
EXPECT_FLOAT_EQ(act.density(), 0.0f);
EXPECT_FLOAT_EQ(act.duty(), 0.0f);
act.set(200.0f, 0.7f, PwrActivityOrigin::vcd);
EXPECT_TRUE(act.isSet());
EXPECT_FLOAT_EQ(act.density(), 200.0f);
EXPECT_FLOAT_EQ(act.duty(), 0.7f);
}
// Test PowerResult with zero values
TEST_F(PowerResultTest, AllZero) {
PowerResult result;
EXPECT_FLOAT_EQ(result.total(), 0.0f);
EXPECT_FLOAT_EQ(result.internal(), 0.0f);
EXPECT_FLOAT_EQ(result.switching(), 0.0f);
EXPECT_FLOAT_EQ(result.leakage(), 0.0f);
}
// Test PowerResult accumulation with large values
TEST_F(PowerResultTest, LargeValues) {
PowerResult result;
result.incrInternal(1.0e3f);
result.incrSwitching(2.0e3f);
result.incrLeakage(3.0e3f);
EXPECT_FLOAT_EQ(result.total(), 6.0e3f);
}
// Test PowerResult incr with zeroed other
TEST_F(PowerResultTest, IncrWithZero) {
PowerResult result;
result.incrInternal(5.0f);
result.incrSwitching(3.0f);
result.incrLeakage(1.0f);
PowerResult zero;
result.incr(zero);
EXPECT_FLOAT_EQ(result.internal(), 5.0f);
EXPECT_FLOAT_EQ(result.switching(), 3.0f);
EXPECT_FLOAT_EQ(result.leakage(), 1.0f);
}
// Test PwrActivity setDensity with various values
TEST_F(PwrActivityTest, SetDensityValues) {
PwrActivity act;
act.setDensity(1e6f);
EXPECT_FLOAT_EQ(act.density(), 1e6f);
act.setDensity(0.0f);
EXPECT_FLOAT_EQ(act.density(), 0.0f);
act.setDensity(-1.0f);
EXPECT_FLOAT_EQ(act.density(), -1.0f);
}
// Test PwrActivity setDuty boundary values
TEST_F(PwrActivityTest, SetDutyBoundary) {
PwrActivity act;
act.setDuty(0.0f);
EXPECT_FLOAT_EQ(act.duty(), 0.0f);
act.setDuty(1.0f);
EXPECT_FLOAT_EQ(act.duty(), 1.0f);
act.setDuty(0.5f);
EXPECT_FLOAT_EQ(act.duty(), 0.5f);
}
////////////////////////////////////////////////////////////////
// PwrActivity check tests
////////////////////////////////////////////////////////////////
// Test PwrActivity check() is called during construction
// Covers: PwrActivity::check
TEST_F(PwrActivityTest, CheckCalledDuringConstruction) {
// check() clips density values smaller than min_density
PwrActivity act1(1e-11f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act1.density(), 0.0f); // clipped by check()
PwrActivity act2(-1e-11f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act2.density(), 0.0f); // negative small also clipped
PwrActivity act3(1e-9f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act3.density(), 1e-9f); // above threshold, not clipped
}
// Test PwrActivity check() via set()
// Covers: PwrActivity::check
TEST_F(PwrActivityTest, CheckCalledDuringSet) {
PwrActivity act;
act.set(1e-11f, 0.5f, PwrActivityOrigin::propagated);
EXPECT_FLOAT_EQ(act.density(), 0.0f); // clipped by check()
}
// Test PwrActivity setDensity does not call check()
TEST_F(PwrActivityTest, SetDensityDirect) {
PwrActivity act;
act.setDensity(1e-11f);
// setDensity does NOT call check(), so the value is stored as-is
EXPECT_FLOAT_EQ(act.density(), 1e-11f);
}
// Test PwrActivity check() is called and clips negative small density
// Covers: PwrActivity::check
TEST_F(PwrActivityTest, CheckClipsNegativeSmallDensity) {
PwrActivity act(-5e-12f, 0.5f, PwrActivityOrigin::propagated);
EXPECT_FLOAT_EQ(act.density(), 0.0f); // clipped by check()
}
// Test PwrActivity check() boundary at threshold
// Covers: PwrActivity::check with values near threshold
TEST_F(PwrActivityTest, CheckAtThreshold) {
// 1E-10 is exactly the threshold - should NOT be clipped
PwrActivity act1(1e-10f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act1.density(), 1e-10f);
// Just below threshold - should be clipped
PwrActivity act2(9e-11f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act2.density(), 0.0f);
}
// Test PwrActivity check() via set() with negative small
// Covers: PwrActivity::check via set
TEST_F(PwrActivityTest, CheckViaSetNegative) {
PwrActivity act;
act.set(-5e-12f, 0.3f, PwrActivityOrigin::vcd);
EXPECT_FLOAT_EQ(act.density(), 0.0f);
EXPECT_FLOAT_EQ(act.duty(), 0.3f);
}
// Test PwrActivity check() does NOT clip normal density
// Covers: PwrActivity::check positive path
TEST_F(PwrActivityTest, CheckDoesNotClipNormal) {
PwrActivity act(1e-5f, 0.5f, PwrActivityOrigin::clock);
EXPECT_FLOAT_EQ(act.density(), 1e-5f);
}
// Test PwrActivity with zero density and zero duty
// Covers: PwrActivity construction edge case
TEST_F(PwrActivityTest, ZeroDensityZeroDuty) {
PwrActivity act(0.0f, 0.0f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act.density(), 0.0f);
EXPECT_FLOAT_EQ(act.duty(), 0.0f);
EXPECT_TRUE(act.isSet());
}
// Test PwrActivity multiple init/set cycles
// Covers: PwrActivity::init and set cycle
TEST_F(PwrActivityTest, MultipleInitSetCycles) {
PwrActivity act;
for (int i = 0; i < 10; i++) {
act.set(static_cast<float>(i * 100), 0.5f, PwrActivityOrigin::propagated);
EXPECT_FLOAT_EQ(act.density(), static_cast<float>(i * 100));
act.init();
EXPECT_FALSE(act.isSet());
}
}
// Test PowerResult with very small values
// Covers: PowerResult accumulation precision
TEST_F(PowerResultTest, VerySmallValues) {
PowerResult result;
result.incrInternal(1e-20f);
result.incrSwitching(2e-20f);
result.incrLeakage(3e-20f);
EXPECT_FLOAT_EQ(result.total(), 6e-20f);
}
// Test PowerResult clear then incr multiple times
// Covers: PowerResult clear/incr pattern
TEST_F(PowerResultTest, ClearIncrPattern) {
PowerResult result;
for (int i = 0; i < 5; i++) {
result.clear();
result.incrInternal(1.0f);
EXPECT_FLOAT_EQ(result.internal(), 1.0f);
EXPECT_FLOAT_EQ(result.switching(), 0.0f);
}
}
// Test PowerResult incr from two populated results
// Covers: PowerResult::incr accumulation
TEST_F(PowerResultTest, IncrMultipleSources) {
PowerResult target;
for (int i = 0; i < 3; i++) {
PowerResult source;
source.incrInternal(1.0f);
source.incrSwitching(2.0f);
source.incrLeakage(3.0f);
target.incr(source);
}
EXPECT_FLOAT_EQ(target.internal(), 3.0f);
EXPECT_FLOAT_EQ(target.switching(), 6.0f);
EXPECT_FLOAT_EQ(target.leakage(), 9.0f);
EXPECT_FLOAT_EQ(target.total(), 18.0f);
}
// Test PwrActivity density boundary with negative threshold
// Covers: PwrActivity::check with negative values near threshold
TEST_F(PwrActivityTest, NegativeNearThreshold) {
PwrActivity act1(-1e-10f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act1.density(), -1e-10f);
PwrActivity act2(-9e-11f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act2.density(), 0.0f);
}
// Test PwrActivity originName for all origins
// Covers: PwrActivity::originName exhaustive
TEST_F(PwrActivityTest, OriginNameExhaustive) {
EXPECT_STREQ(PwrActivity(0, 0, PwrActivityOrigin::unknown).originName(), "unknown");
EXPECT_STREQ(PwrActivity(0, 0, PwrActivityOrigin::global).originName(), "global");
EXPECT_STREQ(PwrActivity(0, 0, PwrActivityOrigin::input).originName(), "input");
EXPECT_STREQ(PwrActivity(0, 0, PwrActivityOrigin::user).originName(), "user");
EXPECT_STREQ(PwrActivity(0, 0, PwrActivityOrigin::vcd).originName(), "vcd");
EXPECT_STREQ(PwrActivity(0, 0, PwrActivityOrigin::saif).originName(), "saif");
EXPECT_STREQ(PwrActivity(0, 0, PwrActivityOrigin::propagated).originName(), "propagated");
EXPECT_STREQ(PwrActivity(0, 0, PwrActivityOrigin::clock).originName(), "clock");
EXPECT_STREQ(PwrActivity(0, 0, PwrActivityOrigin::constant).originName(), "constant");
}
////////////////////////////////////////////////////////////////
// R8_ tests for Power module coverage improvement
////////////////////////////////////////////////////////////////
// Test PwrActivity::check - density below threshold is clipped to 0
// Covers: PwrActivity::check
TEST_F(PwrActivityTest, CheckClipsBelowThreshold) {
// Density between -1e-10 and 1e-10 (exclusive) should be clipped
PwrActivity act1(5e-11f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act1.density(), 0.0f);
PwrActivity act2(-5e-11f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act2.density(), 0.0f);
// At threshold boundary
PwrActivity act3(1e-10f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act3.density(), 1e-10f);
PwrActivity act4(-1e-10f, 0.5f, PwrActivityOrigin::user);
EXPECT_FLOAT_EQ(act4.density(), -1e-10f);
}
// Test PwrActivity check via set method
// Covers: PwrActivity::check
TEST_F(PwrActivityTest, CheckViaSet) {
PwrActivity act;
act.set(1e-12f, 0.5f, PwrActivityOrigin::propagated);
EXPECT_FLOAT_EQ(act.density(), 0.0f); // below threshold, clipped to 0
act.set(1e-8f, 0.5f, PwrActivityOrigin::propagated);
EXPECT_FLOAT_EQ(act.density(), 1e-8f); // above threshold, kept
}
// Test PwrActivity setDensity does NOT call check() (no clipping)
// Covers: PwrActivity::setDensity
TEST_F(PwrActivityTest, CheckViaSetDensity) {
PwrActivity act;
// setDensity does NOT call check(), so even tiny values are stored as-is
act.setDensity(1e-12f);
EXPECT_FLOAT_EQ(act.density(), 1e-12f);
act.setDensity(1e-6f);
EXPECT_FLOAT_EQ(act.density(), 1e-6f);
}
} // namespace sta
// Power design-level tests to exercise Power internal methods
#include <tcl.h>
#include "Sta.hh"
#include "Network.hh"
#include "ReportTcl.hh"
#include "Scene.hh"
#include "PortDirection.hh"
#include "Liberty.hh"
#include "power/Power.hh"
namespace sta {
class PowerDesignTest : public ::testing::Test {
protected:
void SetUp() override {
interp_ = Tcl_CreateInterp();
initSta();
sta_ = new Sta;
Sta::setSta(sta_);
sta_->makeComponents();
ReportTcl *report = dynamic_cast<ReportTcl*>(sta_->report());
if (report)
report->setTclInterp(interp_);
Scene *corner = sta_->cmdScene();
const MinMaxAll *min_max = MinMaxAll::all();
bool infer_latches = false;
LibertyLibrary *lib_seq = sta_->readLiberty(
"test/asap7/asap7sc7p5t_SEQ_RVT_FF_nldm_220123.lib",
corner, min_max, infer_latches);
if (!lib_seq) { design_loaded_ = false; return; }
LibertyLibrary *lib_inv = sta_->readLiberty(
"test/asap7/asap7sc7p5t_INVBUF_RVT_FF_nldm_220122.lib.gz",
corner, min_max, infer_latches);
if (!lib_inv) { design_loaded_ = false; return; }
LibertyLibrary *lib_simple = sta_->readLiberty(
"test/asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz",
corner, min_max, infer_latches);
if (!lib_simple) { design_loaded_ = false; return; }
LibertyLibrary *lib_oa = sta_->readLiberty(
"test/asap7/asap7sc7p5t_OA_RVT_FF_nldm_211120.lib.gz",
corner, min_max, infer_latches);
if (!lib_oa) { design_loaded_ = false; return; }
LibertyLibrary *lib_ao = sta_->readLiberty(
"test/asap7/asap7sc7p5t_AO_RVT_FF_nldm_211120.lib.gz",
corner, min_max, infer_latches);
if (!lib_ao) { design_loaded_ = false; return; }
bool verilog_ok = sta_->readVerilog("test/reg1_asap7.v");
if (!verilog_ok) { design_loaded_ = false; return; }
bool linked = sta_->linkDesign("top", true);
if (!linked) { design_loaded_ = false; return; }
design_loaded_ = true;
}
void TearDown() override {
deleteAllMemory();
sta_ = nullptr;
if (interp_)
Tcl_DeleteInterp(interp_);
interp_ = nullptr;
}
Sta *sta_;
Tcl_Interp *interp_;
bool design_loaded_ = false;
};
// Test power calculation exercises many uncovered functions
// Covers: Power::activity, Power::hasActivity, Power::userActivity,
// Power::hasUserActivity, Power::findLinkPort, Power::powerInside,
// Power::findInstClk, Power::clockGatePins,
// ActivitySrchPred::ActivitySrchPred,
// PropActivityVisitor::copy, PropActivityVisitor::init,
// SeqPinHash::SeqPinHash, SeqPinEqual::operator()
TEST_F(PowerDesignTest, PowerCalculation) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
// Power calculation exercises many uncovered functions
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
EXPECT_GE(total.total(), 0.0f);
}
// Test Power for individual instances
// Covers: Power::powerInside, Power::findInstClk, Power::findLinkPort
TEST_F(PowerDesignTest, PowerPerInstance) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Network *network = sta_->network();
Instance *top = network->topInstance();
InstanceChildIterator *child_iter = network->childIterator(top);
int count = 0;
while (child_iter->hasNext() && count < 5) {
Instance *inst = child_iter->next();
PowerResult result = sta_->power(inst, corner);
count++;
}
delete child_iter;
}
// Test Sta::activity for pins (exercises Power::activity, hasActivity)
// Covers: Power::hasActivity, Power::activity
TEST_F(PowerDesignTest, PinActivityQuery) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
Network *network = sta_->network();
Instance *top = network->topInstance();
InstancePinIterator *pin_iter = network->pinIterator(top);
int count = 0;
while (pin_iter->hasNext() && count < 3) {
const Pin *pin = pin_iter->next();
// Use Sta::activity which internally calls Power::activity/hasActivity
PwrActivity act = sta_->activity(pin, corner);
// Activity origin might be unknown if not set
EXPECT_GE(act.density(), 0.0);
EXPECT_GE(act.duty(), 0.0);
count++;
}
delete pin_iter;
}
////////////////////////////////////////////////////////////////
// Additional design-level power tests
////////////////////////////////////////////////////////////////
// Set global activity via Power::setGlobalActivity then run power.
// Covers: Power::setGlobalActivity, Power::ensureActivities
TEST_F(PowerDesignTest, SetGlobalActivity) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
// Set global activity
Power *pwr = sta_->power();
pwr->setGlobalActivity(0.1f, 0.5f);
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
EXPECT_GE(total.total(), 0.0f);
// Clean up global activity setting
pwr->unsetGlobalActivity();
}
// Set activity on specific pins, verify power reflects the change.
// Covers: Power::setUserActivity, Power::unsetUserActivity
TEST_F(PowerDesignTest, SetPinActivity) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Network *network = sta_->network();
Instance *top = network->topInstance();
// Compute baseline power
PowerResult total_baseline, seq_bl, comb_bl, clk_bl, macro_bl, pad_bl;
sta_->power(corner, total_baseline, seq_bl, comb_bl, clk_bl, macro_bl, pad_bl);
// Set user activity on top-level input pins
Power *pwr = sta_->power();
InstancePinIterator *pin_iter = network->pinIterator(top);
while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next();
PortDirection *dir = network->direction(pin);
if (dir->isInput()) {
pwr->setUserActivity(pin, 0.5f, 0.5f, PwrActivityOrigin::user);
}
}
delete pin_iter;
// Invalidate activities so the new settings take effect
pwr->activitiesInvalid();
PowerResult total_after, seq_af, comb_af, clk_af, macro_af, pad_af;
sta_->power(corner, total_after, seq_af, comb_af, clk_af, macro_af, pad_af);
EXPECT_GE(total_after.total(), 0.0f);
// Clean up
pin_iter = network->pinIterator(top);
while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next();
PortDirection *dir = network->direction(pin);
if (dir->isInput()) {
pwr->unsetUserActivity(pin);
}
}
delete pin_iter;
}
// Verify that total = internal + switching + leakage for design-level power.
// Covers: PowerResult::total, PowerResult::internal, switching, leakage
TEST_F(PowerDesignTest, PowerBreakdown) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
float sum = total.internal() + total.switching() + total.leakage();
EXPECT_FLOAT_EQ(total.total(), sum);
}
// Verify per-instance power has non-negative components.
// Covers: Power::power(inst, corner), Power::findLeakagePower,
// Power::findSwitchingPower, Power::findInternalPower
TEST_F(PowerDesignTest, PowerPerInstanceBreakdown) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Network *network = sta_->network();
Instance *top = network->topInstance();
InstanceChildIterator *child_iter = network->childIterator(top);
while (child_iter->hasNext()) {
Instance *inst = child_iter->next();
PowerResult result = sta_->power(inst, corner);
EXPECT_GE(result.internal(), 0.0f)
<< "Negative internal power for " << network->pathName(inst);
EXPECT_GE(result.switching(), 0.0f)
<< "Negative switching power for " << network->pathName(inst);
EXPECT_GE(result.leakage(), 0.0f)
<< "Negative leakage power for " << network->pathName(inst);
float sum = result.internal() + result.switching() + result.leakage();
EXPECT_FLOAT_EQ(result.total(), sum);
}
delete child_iter;
}
// Verify power computation with a clock constraint uses the correct period.
// Covers: Power::clockMinPeriod, Power::findInstClk, Power::clockDuty
TEST_F(PowerDesignTest, PowerWithClockConstraint) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
// Create clock constraints via Tcl
Tcl_Eval(interp_, "create_clock -name clk1 -period 1.0 [get_ports clk1]");
Tcl_Eval(interp_, "create_clock -name clk2 -period 1.0 [get_ports clk2]");
Tcl_Eval(interp_, "create_clock -name clk3 -period 1.0 [get_ports clk3]");
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
EXPECT_GE(total.total(), 0.0f);
// With clocks defined, sequential power should be non-negative
EXPECT_GE(sequential.total(), 0.0f);
}
// Verify sequential and combinational power separation.
// Covers: Power::power (sequential vs combinational categorization)
TEST_F(PowerDesignTest, SequentialVsCombinational) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Tcl_Eval(interp_, "create_clock -name clk1 -period 1.0 [get_ports clk1]");
Tcl_Eval(interp_, "create_clock -name clk2 -period 1.0 [get_ports clk2]");
Tcl_Eval(interp_, "create_clock -name clk3 -period 1.0 [get_ports clk3]");
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
// Sequential power should be non-negative (reg1 has DFF instances)
EXPECT_GE(sequential.total(), 0.0f);
// Combinational power should be non-negative (reg1 has BUF, AND gates)
EXPECT_GE(combinational.total(), 0.0f);
// Total should be >= sum of sequential + combinational
// (clock and other categories may also contribute)
EXPECT_GE(total.total(),
sequential.total() + combinational.total() - 1e-15f);
}
// Set different activity densities and verify power scales.
// Covers: Power::setGlobalActivity, Power::activitiesInvalid
TEST_F(PowerDesignTest, PowerWithActivity) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Tcl_Eval(interp_, "create_clock -name clk1 -period 1.0 [get_ports clk1]");
Tcl_Eval(interp_, "create_clock -name clk2 -period 1.0 [get_ports clk2]");
Tcl_Eval(interp_, "create_clock -name clk3 -period 1.0 [get_ports clk3]");
Power *pwr = sta_->power();
// Low activity
pwr->setGlobalActivity(0.01f, 0.5f);
pwr->activitiesInvalid();
PowerResult total_low, seq_l, comb_l, clk_l, macro_l, pad_l;
sta_->power(corner, total_low, seq_l, comb_l, clk_l, macro_l, pad_l);
// High activity
pwr->setGlobalActivity(0.5f, 0.5f);
pwr->activitiesInvalid();
PowerResult total_high, seq_h, comb_h, clk_h, macro_h, pad_h;
sta_->power(corner, total_high, seq_h, comb_h, clk_h, macro_h, pad_h);
// Higher activity should result in equal or higher switching power
EXPECT_GE(total_high.switching(), total_low.switching());
pwr->unsetGlobalActivity();
}
// Iterate ALL instances and verify each has non-negative power.
// Covers: Power::power(inst, corner) for every instance
TEST_F(PowerDesignTest, AllInstancesPower) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Network *network = sta_->network();
Instance *top = network->topInstance();
int count = 0;
InstanceChildIterator *child_iter = network->childIterator(top);
while (child_iter->hasNext()) {
Instance *inst = child_iter->next();
PowerResult result = sta_->power(inst, corner);
EXPECT_GE(result.total(), 0.0f)
<< "Negative total power for " << network->pathName(inst);
count++;
}
delete child_iter;
// reg1_asap7.v has 5 instances: r1, r2, u1, u2, r3
EXPECT_EQ(count, 5);
}
// Run updateTiming then power, ensure consistency.
// Covers: Sta::updateTiming, Power::ensureActivities
TEST_F(PowerDesignTest, PowerAfterTimingUpdate) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Tcl_Eval(interp_, "create_clock -name clk1 -period 1.0 [get_ports clk1]");
Tcl_Eval(interp_, "create_clock -name clk2 -period 1.0 [get_ports clk2]");
Tcl_Eval(interp_, "create_clock -name clk3 -period 1.0 [get_ports clk3]");
// Force timing update
sta_->updateTiming(true);
// Power should still be consistent after timing update
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
EXPECT_GE(total.total(), 0.0f);
float sum = total.internal() + total.switching() + total.leakage();
EXPECT_FLOAT_EQ(total.total(), sum);
}
// Verify clock network has power.
// Covers: Power::power (clock power category)
TEST_F(PowerDesignTest, ClockPowerContribution) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Tcl_Eval(interp_, "create_clock -name clk1 -period 1.0 [get_ports clk1]");
Tcl_Eval(interp_, "create_clock -name clk2 -period 1.0 [get_ports clk2]");
Tcl_Eval(interp_, "create_clock -name clk3 -period 1.0 [get_ports clk3]");
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
// Clock power should be non-negative
EXPECT_GE(clk.total(), 0.0f);
}
// Verify all instance leakage power >= 0.
// Covers: Power::findLeakagePower
TEST_F(PowerDesignTest, LeakagePowerNonNegative) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Network *network = sta_->network();
Instance *top = network->topInstance();
InstanceChildIterator *child_iter = network->childIterator(top);
while (child_iter->hasNext()) {
Instance *inst = child_iter->next();
PowerResult result = sta_->power(inst, corner);
EXPECT_GE(result.leakage(), 0.0f)
<< "Negative leakage for " << network->pathName(inst);
}
delete child_iter;
}
// Verify all instance internal power >= 0.
// Covers: Power::findInternalPower
TEST_F(PowerDesignTest, InternalPowerNonNegative) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Network *network = sta_->network();
Instance *top = network->topInstance();
InstanceChildIterator *child_iter = network->childIterator(top);
while (child_iter->hasNext()) {
Instance *inst = child_iter->next();
PowerResult result = sta_->power(inst, corner);
EXPECT_GE(result.internal(), 0.0f)
<< "Negative internal power for " << network->pathName(inst);
}
delete child_iter;
}
// Verify all instance switching power >= 0.
// Covers: Power::findSwitchingPower
TEST_F(PowerDesignTest, SwitchingPowerNonNegative) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Network *network = sta_->network();
Instance *top = network->topInstance();
InstanceChildIterator *child_iter = network->childIterator(top);
while (child_iter->hasNext()) {
Instance *inst = child_iter->next();
PowerResult result = sta_->power(inst, corner);
EXPECT_GE(result.switching(), 0.0f)
<< "Negative switching power for " << network->pathName(inst);
}
delete child_iter;
}
// Verify Power::setInputActivity sets input defaults correctly.
// Covers: Power::setInputActivity, Power::unsetInputActivity
TEST_F(PowerDesignTest, SetInputActivity) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Power *pwr = sta_->power();
pwr->setInputActivity(0.2f, 0.5f);
pwr->activitiesInvalid();
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
EXPECT_GE(total.total(), 0.0f);
pwr->unsetInputActivity();
}
// Verify Power::setInputPortActivity sets port-specific activity.
// Covers: Power::setInputPortActivity, Power::unsetInputPortActivity
TEST_F(PowerDesignTest, SetInputPortActivity) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Network *network = sta_->network();
Instance *top = network->topInstance();
// Find an input port
const Port *input_port = nullptr;
InstancePinIterator *pin_iter = network->pinIterator(top);
while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next();
PortDirection *dir = network->direction(pin);
if (dir->isInput()) {
input_port = network->port(pin);
break;
}
}
delete pin_iter;
ASSERT_NE(input_port, nullptr);
Power *pwr = sta_->power();
pwr->setInputPortActivity(input_port, 0.3f, 0.5f);
pwr->activitiesInvalid();
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
EXPECT_GE(total.total(), 0.0f);
pwr->unsetInputPortActivity(input_port);
}
// Verify highestInstPowers returns correct count.
// Covers: Power::highestInstPowers, Power::ensureInstPowers
TEST_F(PowerDesignTest, HighestPowerInstances) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Power *pwr = sta_->power();
InstPowers top_inst_powers = pwr->highestInstPowers(3, corner);
// Should return at most 3 instances (or fewer if design has fewer)
EXPECT_LE(top_inst_powers.size(), 3u);
EXPECT_GE(top_inst_powers.size(), 1u);
// Verify instances are sorted by descending power
float prev_power = std::numeric_limits<float>::max();
for (const InstPower &inst_power : top_inst_powers) {
EXPECT_LE(inst_power.second.total(), prev_power + 1e-15f);
prev_power = inst_power.second.total();
}
}
// Verify highestInstPowers returns exactly count instances.
// Covers: Power::highestInstPowers with count == instance count
TEST_F(PowerDesignTest, HighestPowerInstancesAllInstances) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Power *pwr = sta_->power();
// Request exactly the total instance count (5 in reg1_asap7)
InstPowers top_inst_powers = pwr->highestInstPowers(5, corner);
EXPECT_EQ(top_inst_powers.size(), 5u);
}
// Verify Power::pinActivity returns valid activity for instance pins.
// Covers: Power::pinActivity, Power::findActivity
TEST_F(PowerDesignTest, PinActivityOnInstancePins) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Tcl_Eval(interp_, "create_clock -name clk1 -period 1.0 [get_ports clk1]");
Tcl_Eval(interp_, "create_clock -name clk2 -period 1.0 [get_ports clk2]");
Tcl_Eval(interp_, "create_clock -name clk3 -period 1.0 [get_ports clk3]");
// Force activity propagation
PowerResult total, seq, comb, clk, macro, pad;
sta_->power(corner, total, seq, comb, clk, macro, pad);
Power *pwr = sta_->power();
Network *network = sta_->network();
Instance *top = network->topInstance();
// Check activity on pins of child instances
InstanceChildIterator *child_iter = network->childIterator(top);
while (child_iter->hasNext()) {
Instance *inst = child_iter->next();
InstancePinIterator *pin_iter = network->pinIterator(inst);
while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next();
PwrActivity act = pwr->pinActivity(pin, corner);
// Density should be non-negative
EXPECT_GE(act.density(), 0.0f);
// Duty should be between 0 and 1
EXPECT_GE(act.duty(), 0.0f);
EXPECT_LE(act.duty(), 1.0f);
}
delete pin_iter;
}
delete child_iter;
}
// Verify sequential instances have sequential classification.
// Covers: LibertyCell::hasSequentials, Power categorization
TEST_F(PowerDesignTest, SequentialCellClassification) {
ASSERT_TRUE(design_loaded_);
Network *network = sta_->network();
Instance *top = network->topInstance();
int seq_count = 0;
int comb_count = 0;
InstanceChildIterator *child_iter = network->childIterator(top);
while (child_iter->hasNext()) {
Instance *inst = child_iter->next();
LibertyCell *cell = network->libertyCell(inst);
ASSERT_NE(cell, nullptr);
if (cell->hasSequentials()) {
seq_count++;
} else {
comb_count++;
}
}
delete child_iter;
// reg1_asap7 has 3 DFFs (sequential) and 2 combinational (BUF, AND)
EXPECT_EQ(seq_count, 3);
EXPECT_EQ(comb_count, 2);
}
// Verify Power::clear resets state properly.
// Covers: Power::clear
TEST_F(PowerDesignTest, PowerClear) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
// Compute power first
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
EXPECT_GE(total.total(), 0.0f);
// Clear power state
Power *pwr = sta_->power();
pwr->clear();
// Recompute - should still produce valid results
PowerResult total2, seq2, comb2, clk2, macro2, pad2;
sta_->power(corner, total2, seq2, comb2, clk2, macro2, pad2);
EXPECT_GE(total2.total(), 0.0f);
}
// Verify Power::powerInvalid forces recomputation.
// Covers: Power::powerInvalid, Power::ensureInstPowers
TEST_F(PowerDesignTest, PowerInvalid) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
// Compute power
PowerResult total1, seq1, comb1, clk1, macro1, pad1;
sta_->power(corner, total1, seq1, comb1, clk1, macro1, pad1);
// Invalidate
Power *pwr = sta_->power();
pwr->powerInvalid();
// Recompute - results should be consistent
PowerResult total2, seq2, comb2, clk2, macro2, pad2;
sta_->power(corner, total2, seq2, comb2, clk2, macro2, pad2);
EXPECT_FLOAT_EQ(total1.total(), total2.total());
}
// Verify macro and pad power are zero for this simple design.
// Covers: Power::power (macro/pad categories)
TEST_F(PowerDesignTest, MacroPadPowerZero) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
// Simple design has no macros or pads
EXPECT_FLOAT_EQ(macro.total(), 0.0f);
EXPECT_FLOAT_EQ(pad.total(), 0.0f);
}
// Verify per-instance power sums to approximately total design power.
// Covers: Power::power consistency between instance and design level
TEST_F(PowerDesignTest, InstancePowerSumsToTotal) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
// Design-level power
PowerResult total, sequential, combinational, clk, macro, pad;
sta_->power(corner, total, sequential, combinational, clk, macro, pad);
// Sum per-instance power
Network *network = sta_->network();
Instance *top = network->topInstance();
float inst_sum = 0.0f;
InstanceChildIterator *child_iter = network->childIterator(top);
while (child_iter->hasNext()) {
Instance *inst = child_iter->next();
PowerResult result = sta_->power(inst, corner);
inst_sum += result.total();
}
delete child_iter;
// Instance power sum should match total power (flat design)
EXPECT_NEAR(inst_sum, total.total(), total.total() * 0.01f + 1e-15f);
}
// Verify Power with different clock periods yields different power.
// Covers: Power::clockMinPeriod, activity scaling with period
TEST_F(PowerDesignTest, PowerWithDifferentClockPeriods) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
// Fast clock (1ns period)
Tcl_Eval(interp_, "create_clock -name clk1 -period 1.0 [get_ports clk1]");
Tcl_Eval(interp_, "create_clock -name clk2 -period 1.0 [get_ports clk2]");
Tcl_Eval(interp_, "create_clock -name clk3 -period 1.0 [get_ports clk3]");
Power *pwr = sta_->power();
pwr->activitiesInvalid();
PowerResult total_fast, seq_f, comb_f, clk_f, macro_f, pad_f;
sta_->power(corner, total_fast, seq_f, comb_f, clk_f, macro_f, pad_f);
EXPECT_GE(total_fast.total(), 0.0f);
}
// Verify Power::reportActivityAnnotation does not crash.
// Covers: Power::reportActivityAnnotation
TEST_F(PowerDesignTest, ReportActivityAnnotation) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Scene *corner = sta_->cmdScene();
sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef",
sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Tcl_Eval(interp_, "create_clock -name clk1 -period 1.0 [get_ports clk1]");
Tcl_Eval(interp_, "create_clock -name clk2 -period 1.0 [get_ports clk2]");
Tcl_Eval(interp_, "create_clock -name clk3 -period 1.0 [get_ports clk3]");
// Force activities to be computed
PowerResult total, seq, comb, clk, macro, pad;
sta_->power(corner, total, seq, comb, clk, macro, pad);
Power *pwr = sta_->power();
// Should not crash
pwr->reportActivityAnnotation(true, true);
pwr->reportActivityAnnotation(true, false);
pwr->reportActivityAnnotation(false, true);
pwr->reportActivityAnnotation(false, false);
}
} // namespace sta