mirror of
https://github.com/The-OpenROAD-Project/OpenSTA.git
synced 2026-08-31 18:14:47 +02:00
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:
co-authored by
Claude Opus 4.6
parent
547737f71e
commit
e57c8043cd
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user