test: Apply review feedback - part3

Remove unnecessary catch blocks from Tcl test files across all modules,
add report_checks after each set_wire_load_model in liberty_wireload,
rewrite liberty_sky130_corners for actual multi-corner timing analysis
with define_corners, and expand C++ tests (TestSearchIncremental 8→36,
TestPower 71→96, TestSpice 98→126 tests).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
Jaehyun Kim
2026-02-20 13:05:07 +09:00
co-authored by Claude Opus 4.6
parent 547737f71e
commit e57c8043cd
62 changed files with 3600 additions and 1244 deletions
+675
View File
@@ -776,6 +776,8 @@ TEST_F(PwrActivityTest, CheckViaSetDensity) {
#include "Network.hh"
#include "ReportTcl.hh"
#include "Corner.hh"
#include "PortDirection.hh"
#include "Liberty.hh"
#include "power/Power.hh"
namespace sta {
@@ -911,4 +913,677 @@ TEST_F(PowerDesignTest, PinActivityQuery) {
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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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 highestPowerInstances returns correct count.
// Covers: Power::highestPowerInstances, Power::ensureInstPowers
TEST_F(PowerDesignTest, HighestPowerInstances) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("test/reg1_asap7.spef", sta_->network()->topInstance(), corner,
MinMaxAll::all(), false, false, 1.0f, true);
Power *pwr = sta_->power();
InstanceSeq top_instances = pwr->highestPowerInstances(3, corner);
// Should return at most 3 instances (or fewer if design has fewer)
EXPECT_LE(top_instances.size(), 3u);
EXPECT_GE(top_instances.size(), 1u);
// Verify instances are sorted by descending power
Network *network = sta_->network();
float prev_power = std::numeric_limits<float>::max();
for (const Instance *inst : top_instances) {
PowerResult result = sta_->power(inst, corner);
EXPECT_LE(result.total(), prev_power + 1e-15f);
prev_power = result.total();
}
}
// Verify highestPowerInstances returns exactly count instances.
// Covers: Power::highestPowerInstances with count == instance count
TEST_F(PowerDesignTest, HighestPowerInstancesAllInstances) {
ASSERT_TRUE(design_loaded_);
sta_->ensureGraph();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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)
InstanceSeq top_instances = pwr->highestPowerInstances(5, corner);
EXPECT_EQ(top_instances.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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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);
// 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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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();
Corner *corner = sta_->cmdCorner();
sta_->readSpef("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