This commit is contained in:
James Cherry 2019-04-29 08:39:05 -07:00
parent 30974b0604
commit d1a602cefc
14 changed files with 696 additions and 134 deletions

View File

@ -16,7 +16,7 @@
cmake_minimum_required (VERSION 3.9) cmake_minimum_required (VERSION 3.9)
project(STA VERSION 2.0.14) project(STA VERSION 2.0.15)
set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)

View File

@ -9,18 +9,36 @@ Release 2.0.0 2018/09/28
Builds using Autotools/configure are no longer supported. Builds using Autotools/configure are no longer supported.
Use CMake as documented in README.mb. Use CMake as documented in README.mb.
....
The check_timing command -no_output_delay checks output ports for The check_timing command -no_output_delay checks output ports for
set_output_delay. set_output_delay.
....
The report_power command reports the power consumption of the design The report_power command reports the power consumption of the design
or a specific instance. or a specific instance.
report_power [-instance inst] [-digits digits] [> filename] [>> filename] report_power [-instances inst] [-digits digits] [> filename] [>> filename]
Report power used by the design or a specific instance. The internal, The internal, switching, leakage and total power are reported. Design
switching, leakage and total power are reported. Design power is power is reported separately for combinational, sequential, macro and
reported separately for combinational, sequential, macro and pad pad groups.
groups.
Use -instances to report power for a specific instance.
Use the set_power_activity command to specify activity/duty
globally using -global, the input port default using -input,
or for input ports using -input_ports, or pins using -pins.
set_power_activity [-global]
[-input]
[-input_ports ports]
[-pins pins]
[-activiity activity]
[-duty duty]
....
The write_path_spice command writes a spice netlist for a timing path. The write_path_spice command writes a spice netlist for a timing path.
@ -46,6 +64,8 @@ and ground names are specified with the -power and -ground arguments.
The spice netlist includes a piecewise linear voltage source at the The spice netlist includes a piecewise linear voltage source at the
input and .measure statement for each gate delay and pin slew. input and .measure statement for each gate delay and pin slew.
....
The report_checks, find_timing_paths commands now support an The report_checks, find_timing_paths commands now support an
-unconstrained flag. -unconstrained flag.
@ -55,6 +75,8 @@ The report_checks, find_timing_paths commands now support an
The sta_report_unconstrained_paths variable will be supported for The sta_report_unconstrained_paths variable will be supported for
for compatibility in this current release. for compatibility in this current release.
....
The read_parasitics command has been renamed read_spef and no longer The read_parasitics command has been renamed read_spef and no longer
supports the SPF format. supports the SPF format.

Binary file not shown.

Binary file not shown.

View File

@ -20,7 +20,7 @@
#define STA_DELAY_H #define STA_DELAY_H
#if (SSTA == 1) #if (SSTA == 1)
// Delays are Normal PDFs with early/late sigma. // Delays are Normal PDFs.
#include "DelayNormal1.hh" #include "DelayNormal1.hh"
#elif (SSTA == 2) #elif (SSTA == 2)
// Delays are Normal PDFs with early/late sigma. // Delays are Normal PDFs with early/late sigma.

View File

@ -52,20 +52,20 @@ delayInitValue(const MinMax *min_max)
Delay::Delay() : Delay::Delay() :
mean_(0.0), mean_(0.0),
sigma2_{0.0} sigma2_(0.0)
{ {
} }
Delay::Delay(float mean) : Delay::Delay(float mean) :
mean_(mean), mean_(mean),
sigma2_{0.0} sigma2_(0.0)
{ {
} }
Delay::Delay(float mean, Delay::Delay(float mean,
float sigma2) : float sigma2) :
mean_(mean), mean_(mean),
sigma2_{sigma2} sigma2_(sigma2)
{ {
} }
@ -155,7 +155,7 @@ void
Delay::operator-=(const Delay &delay) Delay::operator-=(const Delay &delay)
{ {
mean_ -= delay.mean_; mean_ -= delay.mean_;
sigma2_ -= delay.sigma2_; sigma2_ += delay.sigma2_;
} }
bool bool
@ -348,11 +348,10 @@ operator/(float delay1,
Delay Delay
operator*(const Delay &delay1, operator*(const Delay &delay1,
float scale) float delay2)
{ {
float scale2 = square(scale); return Delay(delay1.mean() * delay2,
return Delay(delay1.mean() * scale, delay1.sigma2() * delay2);
delay1.sigma2() * scale2);
} }
float float

View File

@ -174,8 +174,8 @@ void
Delay::operator-=(const Delay &delay) Delay::operator-=(const Delay &delay)
{ {
mean_ -= delay.mean_; mean_ -= delay.mean_;
sigma2_[early_index] -= delay.sigma2_[early_index]; sigma2_[early_index] += delay.sigma2_[early_index];
sigma2_[late_index] -= delay.sigma2_[late_index]; sigma2_[late_index] += delay.sigma2_[late_index];
} }
bool bool
@ -374,12 +374,11 @@ operator/(float delay1,
Delay Delay
operator*(const Delay &delay1, operator*(const Delay &delay1,
float scale) float delay2)
{ {
float scale2 = square(scale); return Delay(delay1.mean() * delay2,
return Delay(delay1.mean() * scale, delay1.sigma2()Early * delay2,
delay1.sigma2Early() * scale2, delay1.sigma2Late() * delay2);
delay1.sigma2Late() * scale2);
} }
float float

View File

@ -878,9 +878,6 @@ LibertyCell::~LibertyCell()
pg_port_map_.deleteContents(); pg_port_map_.deleteContents();
} }
// Multiple timing arc sets (buses bits or a related_ports list)
// can share the same TimingAttrs values (model, cond, and sdf_conds),
// so collect them into a set so they are only deleted once.
void void
LibertyCell::deleteTimingArcAttrs() LibertyCell::deleteTimingArcAttrs()
{ {
@ -1176,6 +1173,7 @@ LibertyCell::makeTimingArcMap(Report *)
// match->from()->name(), // match->from()->name(),
// match->to()->name(), // match->to()->name(),
// match->role()->asString()); // match->role()->asString());
delete arc_set;
} }
else else
// Shift arc sets down to fill holes left by removed duplicates. // Shift arc sets down to fill holes left by removed duplicates.

View File

@ -17,23 +17,30 @@
#include <algorithm> // max #include <algorithm> // max
#include "Machine.hh" #include "Machine.hh"
#include "Debug.hh" #include "Debug.hh"
#include "EnumNameMap.hh"
#include "Units.hh" #include "Units.hh"
#include "Transition.hh" #include "Transition.hh"
#include "MinMax.hh" #include "MinMax.hh"
#include "Clock.hh"
#include "TimingRole.hh"
#include "Liberty.hh" #include "Liberty.hh"
#include "InternalPower.hh" #include "InternalPower.hh"
#include "LeakagePower.hh" #include "LeakagePower.hh"
#include "Sequential.hh"
#include "TimingArc.hh" #include "TimingArc.hh"
#include "FuncExpr.hh" #include "FuncExpr.hh"
#include "PortDirection.hh" #include "PortDirection.hh"
#include "Network.hh" #include "Network.hh"
#include "Graph.hh" #include "Graph.hh"
#include "Sim.hh"
#include "Corner.hh" #include "Corner.hh"
#include "Sdc.hh"
#include "DcalcAnalysisPt.hh" #include "DcalcAnalysisPt.hh"
#include "GraphDelayCalc.hh" #include "GraphDelayCalc.hh"
#include "PathVertex.hh" #include "PathVertex.hh"
#include "Clock.hh" #include "Levelize.hh"
#include "Sim.hh"
#include "Search.hh"
#include "Bfs.hh"
#include "Power.hh" #include "Power.hh"
// Related liberty not supported: // Related liberty not supported:
@ -54,23 +61,65 @@ typedef Map<const char*, SumCount, CharPtrLess> SupplySumCounts;
Power::Power(Sta *sta) : Power::Power(Sta *sta) :
StaState(sta), StaState(sta),
sta_(sta), global_activity_{0.0, 0.0, PwrActivityOrigin::unknown},
default_signal_toggle_rate_(.1) input_activity_{0.1, 0.5, PwrActivityOrigin::input},
default_activity_(.1),
activities_valid_(false)
{ {
} }
float
Power::defaultSignalToggleRate()
{
return default_signal_toggle_rate_;
}
void void
Power::setDefaultSignalToggleRate(float rate) Power::setGlobalActivity(float activity,
float duty)
{ {
default_signal_toggle_rate_ = rate; global_activity_.set(activity, duty, PwrActivityOrigin::global);
activities_valid_ = false;
} }
void
Power::setInputActivity(float activity,
float duty)
{
input_activity_.set(activity, duty, PwrActivityOrigin::input);
activities_valid_ = false;
}
void
Power::setInputPortActivity(const Port *input_port,
float activity,
float duty)
{
Instance *top_inst = network_->topInstance();
const Pin *pin = network_->findPin(top_inst, input_port);
if (pin) {
activity_map_[pin] = {activity, duty, PwrActivityOrigin::user};
activities_valid_ = false;
}
}
PwrActivity &
Power::pinActivity(const Pin *pin)
{
return activity_map_[pin];
}
void
Power::setPinActivity(const Pin *pin,
float activity,
float duty,
PwrActivityOrigin origin)
{
activity_map_[pin] = {activity, duty, origin};
activities_valid_ = false;
}
void
Power::setPinActivity(const Pin *pin,
PwrActivity &activity)
{
activity_map_[pin] = activity;
activities_valid_ = false;
}
void void
Power::power(const Corner *corner, Power::power(const Corner *corner,
@ -86,6 +135,8 @@ Power::power(const Corner *corner,
combinational.clear(); combinational.clear();
macro.clear(); macro.clear();
pad.clear(); pad.clear();
preamble();
LeafInstanceIterator *inst_iter = network_->leafInstanceIterator(); LeafInstanceIterator *inst_iter = network_->leafInstanceIterator();
while (inst_iter->hasNext()) { while (inst_iter->hasNext()) {
Instance *inst = inst_iter->next(); Instance *inst = inst_iter->next();
@ -107,8 +158,6 @@ Power::power(const Corner *corner,
delete inst_iter; delete inst_iter;
} }
////////////////////////////////////////////////////////////////
void void
Power::power(const Instance *inst, Power::power(const Instance *inst,
const Corner *corner, const Corner *corner,
@ -116,9 +165,267 @@ Power::power(const Instance *inst,
PowerResult &result) PowerResult &result)
{ {
LibertyCell *cell = network_->libertyCell(inst); LibertyCell *cell = network_->libertyCell(inst);
if (cell) if (cell) {
preamble();
power(inst, cell, corner, result); power(inst, cell, corner, result);
} }
}
////////////////////////////////////////////////////////////////
class ActivitySrchPred : public SearchPred
{
public:
explicit ActivitySrchPred(const StaState *sta);
virtual bool searchFrom(const Vertex *from_vertex);
virtual bool searchThru(Edge *edge);
virtual bool searchTo(const Vertex *);
protected:
const StaState *sta_;
};
ActivitySrchPred::ActivitySrchPred(const StaState *sta) :
sta_(sta)
{
}
bool
ActivitySrchPred::searchFrom(const Vertex *)
{
return true;
}
bool
ActivitySrchPred::searchThru(Edge *edge)
{
auto role = edge->role();
return !(edge->isDisabledLoop()
|| role->isTimingCheck()
|| role == TimingRole::regClkToQ());
}
bool
ActivitySrchPred::searchTo(const Vertex *)
{
return true;
}
////////////////////////////////////////////////////////////////
class PropActivityVisitor : public VertexVisitor, StaState
{
public:
PropActivityVisitor(Power *power,
BfsFwdIterator *bfs);
virtual VertexVisitor *copy();
virtual void visit(Vertex *vertex);
private:
Power *power_;
BfsFwdIterator *bfs_;
};
PropActivityVisitor::PropActivityVisitor(Power *power,
BfsFwdIterator *bfs) :
StaState(power),
power_(power),
bfs_(bfs)
{
}
VertexVisitor *
PropActivityVisitor::copy()
{
return new PropActivityVisitor(power_, bfs_);
}
PwrActivity
Power::evalActivity(FuncExpr *expr,
const Instance *inst)
{
switch (expr->op()) {
case FuncExpr::op_port: {
Pin *pin = network_->findPin(inst, expr->port()->name());
if (pin)
return pinActivity(pin);
}
case FuncExpr::op_not: {
PwrActivity activity1 = evalActivity(expr->left(), inst);
return PwrActivity(activity1.activity(),
1.0 - activity1.duty(),
PwrActivityOrigin::propagated);
}
case FuncExpr::op_or: {
PwrActivity activity1 = evalActivity(expr->left(), inst);
PwrActivity activity2 = evalActivity(expr->right(), inst);
float p1 = 1.0 - activity1.duty();
float p2 = 1.0 - activity2.duty();
return PwrActivity(activity1.activity() * p2
+ activity2.activity() * p1,
1.0 - p1 * p2,
PwrActivityOrigin::propagated);
}
case FuncExpr::op_and: {
PwrActivity activity1 = evalActivity(expr->left(), inst);
PwrActivity activity2 = evalActivity(expr->right(), inst);
float p1 = activity1.duty();
float p2 = activity2.duty();
return PwrActivity(activity1.activity() * p2 + activity2.activity() * p1,
p1 * p2,
PwrActivityOrigin::propagated);
}
case FuncExpr::op_xor: {
PwrActivity activity1 = evalActivity(expr->left(), inst);
PwrActivity activity2 = evalActivity(expr->right(), inst);
float p1 = activity1.duty() * (1.0 - activity2.duty());
float p2 = activity2.duty() * (1.0 - activity1.duty());
return PwrActivity(activity1.activity() * p1 + activity2.activity() * p2,
p1 + p2,
PwrActivityOrigin::propagated);
}
case FuncExpr::op_one:
return PwrActivity(0.0, 1.0, PwrActivityOrigin::constant);
case FuncExpr::op_zero:
return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant);
}
return PwrActivity();
}
void
PropActivityVisitor::visit(Vertex *vertex)
{
auto pin = vertex->pin();
debugPrint1(debug_, "power", 3, "activity %s\n",
vertex->name(network_));
if (network_->isLoad(pin)) {
VertexInEdgeIterator edge_iter(vertex, graph_);
if (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->isWire()) {
Vertex *from_vertex = edge->from(graph_);
const Pin *from_pin = from_vertex->pin();
PwrActivity &from_activity = power_->pinActivity(from_pin);
PwrActivity to_activity(from_activity.activity(),
from_activity.duty(),
PwrActivityOrigin::propagated);
power_->setPinActivity(pin, to_activity);
}
}
}
if (network_->isDriver(pin)) {
LibertyPort *port = network_->libertyPort(pin);
if (port) {
FuncExpr *func = port->function();
Instance *inst = network_->instance(pin);
PwrActivity activity = power_->evalActivity(func, inst);
power_->setPinActivity(pin, activity);
}
}
bfs_->enqueueAdjacentVertices(vertex);
}
////////////////////////////////////////////////////////////////
void
Power::preamble()
{
ensureActivities();
}
void
Power::ensureActivities()
{
if (!global_activity_.isSet()) {
if (!activities_valid_) {
ActivitySrchPred activity_srch_pred(this);
BfsFwdIterator bfs(BfsIndex::other, &activity_srch_pred, this);
seedActivities(bfs);
PropActivityVisitor visitor(this, &bfs);
bfs.visit(levelize_->maxLevel(), &visitor);
// Propagate activiities across register D->Q.
seedRegOutputActivities(bfs);
bfs.visit(levelize_->maxLevel(), &visitor);
activities_valid_ = true;
}
}
}
void
Power::seedActivities(BfsFwdIterator &bfs)
{
Instance *top_inst = network_->topInstance();
InstancePinIterator *pin_iter = network_->pinIterator(top_inst);
while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next();
if (network_->direction(pin)->isAnyInput()) {
// Clock activities are baked in.
if (!sdc_->isClock(pin)) {
PwrActivity &activity = pinActivity(pin);
PwrActivityOrigin origin = activity.origin();
// Default inputs without explicit activities to the input default.
if (origin != PwrActivityOrigin::user)
setPinActivity(pin, input_activity_);
Vertex *vertex = graph_->pinDrvrVertex(pin);
bfs.enqueueAdjacentVertices(vertex);
}
}
}
delete pin_iter;
}
void
Power::seedRegOutputActivities(BfsFwdIterator &bfs)
{
LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator();
while (leaf_iter->hasNext()) {
auto inst = leaf_iter->next();
auto cell = network_->libertyCell(inst);
if (cell) {
LibertyCellSequentialIterator seq_iter(cell);
while (seq_iter.hasNext()) {
Sequential *seq = seq_iter.next();
seedRegOutputActivities(inst, seq, seq->output(), false);
seedRegOutputActivities(inst, seq, seq->outputInv(), true);
// Enqueue register output pins with functions that reference
// the sequential internal pins (IQ, IQN).
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
auto pin = pin_iter->next();
auto port = network_->libertyPort(pin);
auto func = port->function();
if (func) {
Vertex *vertex = graph_->pinDrvrVertex(pin);
if (func->port() == seq->output()
|| func->port() == seq->outputInv())
bfs.enqueue(vertex);
}
}
delete pin_iter;
}
}
}
delete leaf_iter;
}
void
Power::seedRegOutputActivities(Instance *reg,
Sequential *seq,
LibertyPort *output,
bool invert)
{
const Pin *pin = network_->findPin(reg, output);
if (pin) {
PwrActivity activity = evalActivity(seq->data(), reg);
if (invert)
activity.set(activity.activity(),
1.0 - activity.duty(),
activity.origin());
setPinActivity(pin, activity);
}
}
////////////////////////////////////////////////////////////////
void void
Power::power(const Instance *inst, Power::power(const Instance *inst,
@ -137,13 +444,11 @@ Power::power(const Instance *inst,
float load_cap = to_port->direction()->isAnyOutput() float load_cap = to_port->direction()->isAnyOutput()
? graph_delay_calc_->loadCap(to_pin, TransRiseFall::rise(), dcalc_ap) ? graph_delay_calc_->loadCap(to_pin, TransRiseFall::rise(), dcalc_ap)
: 0.0; : 0.0;
float activity1; PwrActivity activity = findActivity(to_pin, inst_clk);
bool is_clk;
activity(to_pin, inst_clk, activity1, is_clk);
if (to_port->direction()->isAnyOutput()) if (to_port->direction()->isAnyOutput())
findSwitchingPower(cell, to_port, activity1, load_cap, findSwitchingPower(cell, to_port, activity, load_cap,
dcalc_ap, result); dcalc_ap, result);
findInternalPower(inst, cell, to_port, activity1, findInternalPower(inst, cell, to_port, activity,
load_cap, dcalc_ap, result); load_cap, dcalc_ap, result);
} }
delete pin_iter; delete pin_iter;
@ -157,10 +462,8 @@ Power::findInstClk(const Instance *inst)
InstancePinIterator *pin_iter = network_->pinIterator(inst); InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) { while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next(); const Pin *pin = pin_iter->next();
const Clock *clk = nullptr; const Clock *clk = findClk(pin);
bool is_clk; if (clk)
findClk(pin, clk, is_clk);
if (is_clk)
inst_clk = clk; inst_clk = clk;
} }
delete pin_iter; delete pin_iter;
@ -171,7 +474,7 @@ void
Power::findInternalPower(const Instance *inst, Power::findInternalPower(const Instance *inst,
LibertyCell *cell, LibertyCell *cell,
const LibertyPort *to_port, const LibertyPort *to_port,
float activity, PwrActivity &activity,
float load_cap, float load_cap,
const DcalcAnalysisPt *dcalc_ap, const DcalcAnalysisPt *dcalc_ap,
// Return values. // Return values.
@ -183,10 +486,9 @@ Power::findInternalPower(const Instance *inst,
cell->name()); cell->name());
SupplySumCounts supply_sum_counts; SupplySumCounts supply_sum_counts;
const Pvt *pvt = dcalc_ap->operatingConditions(); const Pvt *pvt = dcalc_ap->operatingConditions();
float duty = 1.0; debugPrint1(debug_, "power", 2, " cap = %s\n",
debugPrint2(debug_, "power", 2, " cap = %s duty = %.1f\n", units_->capacitanceUnit()->asString(load_cap));
units_->capacitanceUnit()->asString(load_cap), debugPrint0(debug_, "power", 2, " when act/ns duty energy power\n");
duty);
LibertyCellInternalPowerIterator pwr_iter(cell); LibertyCellInternalPowerIterator pwr_iter(cell);
while (pwr_iter.hasNext()) { while (pwr_iter.hasNext()) {
InternalPower *pwr = pwr_iter.next(); InternalPower *pwr = pwr_iter.next();
@ -197,33 +499,37 @@ Power::findInternalPower(const Instance *inst,
from_port = to_port; from_port = to_port;
const Pin *from_pin = network_->findPin(inst, from_port); const Pin *from_pin = network_->findPin(inst, from_port);
Vertex *from_vertex = graph_->pinLoadVertex(from_pin); Vertex *from_vertex = graph_->pinLoadVertex(from_pin);
FuncExpr *when = pwr->when();
PwrActivity when_activity = when ? evalActivity(when, inst)
: findActivity(from_pin);
float duty = when_activity.duty();
float port_energy = 0.0;
float port_internal = 0.0; float port_internal = 0.0;
TransRiseFallIterator tr_iter; TransRiseFallIterator tr_iter;
while (tr_iter.hasNext()) { while (tr_iter.hasNext()) {
TransRiseFall *to_tr = tr_iter.next(); TransRiseFall *to_tr = tr_iter.next();
// Should use unateness to find from_tr. // Should use unateness to find from_tr.
TransRiseFall *from_tr = to_tr; TransRiseFall *from_tr = to_tr;
float slew = delayAsFloat(sta_->vertexSlew(from_vertex, float slew = delayAsFloat(graph_->slew(from_vertex,
from_tr, dcalc_ap)); from_tr,
float energy; dcalc_ap->index()));
if (from_port) float tr_energy = (from_port)
energy = pwr->power(to_tr, pvt, slew, load_cap); ? pwr->power(to_tr, pvt, slew, load_cap)
else : pwr->power(to_tr, pvt, 0.0, 0.0);
energy = pwr->power(to_tr, pvt, 0.0, 0.0); float tr_internal = tr_energy * activity.activity();
float tr_internal = energy * activity * duty; port_energy += tr_energy;
port_internal += tr_internal; port_internal += tr_internal;
debugPrint5(debug_, "power", 2, " %s -> %s %s %s (%s)\n", }
debugPrint8(debug_, "power", 2, " %s -> %s %s %.2f %.2f %9.2e %9.2e %s\n",
from_port->name(), from_port->name(),
to_tr->shortName(),
to_port->name(), to_port->name(),
pwr->when() ? pwr->when()->asString() : " ", pwr->when() ? pwr->when()->asString() : " ",
related_pg_pin ? related_pg_pin : "(no pg_pin)"); activity.activity() * 1e-9,
debugPrint4(debug_, "power", 2, " slew = %s activity = %.2f/ns energy = %.5g pwr = %.5g\n", duty,
units_->timeUnit()->asString(slew), port_energy,
activity * 1e-9, port_internal,
energy, related_pg_pin ? related_pg_pin : "no pg_pin");
tr_internal);
}
// Sum/count internal power arcs by supply to average across conditions. // Sum/count internal power arcs by supply to average across conditions.
SumCount &supply_sum_count = supply_sum_counts[related_pg_pin]; SumCount &supply_sum_count = supply_sum_counts[related_pg_pin];
// Average rise/fall internal power. // Average rise/fall internal power.
@ -239,7 +545,9 @@ Power::findInternalPower(const Instance *inst,
internal += supply_internal / (supply_count > 0 ? supply_count : 1); internal += supply_internal / (supply_count > 0 ? supply_count : 1);
} }
debugPrint1(debug_, "power", 2, " internal = %.5g\n", internal); debugPrint2(debug_, "power", 2, " %s internal = %.3e\n",
to_port->name(),
internal);
result.setInternal(result.internal() + internal); result.setInternal(result.internal() + internal);
} }
@ -303,44 +611,63 @@ Power::findLeakagePower(const Instance *,
void void
Power::findSwitchingPower(LibertyCell *cell, Power::findSwitchingPower(LibertyCell *cell,
const LibertyPort *to_port, const LibertyPort *to_port,
float activity, PwrActivity &activity,
float load_cap, float load_cap,
const DcalcAnalysisPt *dcalc_ap, const DcalcAnalysisPt *dcalc_ap,
// Return values. // Return values.
PowerResult &result) PowerResult &result)
{ {
float volt = voltage(cell, to_port, dcalc_ap); float volt = voltage(cell, to_port, dcalc_ap);
float switching = .5 * load_cap * volt * volt * activity; float switching = .5 * load_cap * volt * volt * activity.activity();
debugPrint5(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e\n", debugPrint5(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e\n",
cell->name(), cell->name(),
to_port->name(), to_port->name(),
activity, activity.activity(),
volt, volt,
switching); switching);
volt = voltage(cell, to_port, dcalc_ap); volt = voltage(cell, to_port, dcalc_ap);
result.setSwitching(result.switching() + switching); result.setSwitching(result.switching() + switching);
} }
void PwrActivity
Power::activity(const Pin *pin, Power::findActivity(const Pin *pin)
const Clock *inst_clk,
// Return values.
float &activity,
bool &is_clk)
{ {
const Clock *clk = inst_clk; const Instance *inst = network_->instance(pin);
findClk(pin, clk, is_clk); const Clock *inst_clk = findInstClk(inst);
activity = 0.0; return findActivity(pin, inst_clk);
}
PwrActivity
Power::findActivity(const Pin *pin,
const Clock *inst_clk)
{
const Clock *clk = findClk(pin);
if (clk == nullptr)
clk = inst_clk;
if (clk) { if (clk) {
float period = clk->period(); float period = clk->period();
if (period > 0.0) { if (period > 0.0) {
if (is_clk) Vertex *vertex = graph_->pinLoadVertex(pin);
activity = 2.0 / period; if (search_->isClock(vertex))
else return PwrActivity(2.0 / period, 0.5, PwrActivityOrigin::clock);
activity = default_signal_toggle_rate_ / period; else if (global_activity_.isSet())
return PwrActivity(global_activity_.activity() / period,
0.5, PwrActivityOrigin::global);
else {
if (activity_map_.hasKey(pin)) {
PwrActivity &activity = activity_map_[pin];
if (activity.origin() != PwrActivityOrigin::unknown)
return PwrActivity(activity.activity() / period,
activity.duty(),
activity.origin());
}
return PwrActivity(default_activity_ / period,
0.5, PwrActivityOrigin::defaulted);
} }
} }
} }
return PwrActivity();
}
float float
Power::voltage(LibertyCell *cell, Power::voltage(LibertyCell *cell,
@ -370,13 +697,10 @@ Power::voltage(LibertyCell *cell,
return 0.0; return 0.0;
} }
void const Clock *
Power::findClk(const Pin *to_pin, Power::findClk(const Pin *to_pin)
// Return values.
const Clock *&clk,
bool &is_clk)
{ {
is_clk = false; const Clock *clk = nullptr;
Vertex *to_vertex = graph_->pinDrvrVertex(to_pin); Vertex *to_vertex = graph_->pinDrvrVertex(to_pin);
VertexPathIterator path_iter(to_vertex, this); VertexPathIterator path_iter(to_vertex, this);
while (path_iter.hasNext()) { while (path_iter.hasNext()) {
@ -386,9 +710,8 @@ Power::findClk(const Pin *to_pin,
&& (clk == nullptr && (clk == nullptr
|| path_clk->period() < clk->period())) || path_clk->period() < clk->period()))
clk = path_clk; clk = path_clk;
if (path->isClock(this))
is_clk = true;
} }
return clk;
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
@ -440,4 +763,54 @@ PowerResult::incr(PowerResult &result)
leakage_ += result.leakage_; leakage_ += result.leakage_;
} }
////////////////////////////////////////////////////////////////
static EnumNameMap<PwrActivityOrigin> pwr_activity_origin_map =
{{PwrActivityOrigin::global, "global"},
{PwrActivityOrigin::input, "input"},
{PwrActivityOrigin::user, "user"},
{PwrActivityOrigin::propagated, "propagated"},
{PwrActivityOrigin::clock, "clock"},
{PwrActivityOrigin::constant, "constant"},
{PwrActivityOrigin::defaulted, "defaulted"},
{PwrActivityOrigin::unknown, "unknown"}};
PwrActivity::PwrActivity(float activity,
float duty,
PwrActivityOrigin origin) :
activity_(activity),
duty_(duty),
origin_(origin)
{
}
PwrActivity::PwrActivity() :
activity_(0.0),
duty_(0.0),
origin_(PwrActivityOrigin::unknown)
{
}
void
PwrActivity::set(float activity,
float duty,
PwrActivityOrigin origin)
{
activity_ = activity;
duty_ = duty;
origin_ = origin;
}
bool
PwrActivity::isSet() const
{
return origin_ != PwrActivityOrigin::unknown;
}
const char *
PwrActivity::originName() const
{
return pwr_activity_origin_map.find(origin_);
}
} // namespace } // namespace

View File

@ -22,6 +22,46 @@
namespace sta { namespace sta {
class PowerResult; class PowerResult;
class PwrActivity;
class PropActivityVisitor;
class BfsFwdIterator;
typedef UnorderedMap<const Pin*,PwrActivity> PwrActivityMap;
enum class PwrActivityOrigin
{
global,
input,
user,
propagated,
clock,
constant,
defaulted, // temporary
unknown
};
class PwrActivity
{
public:
PwrActivity();
PwrActivity(float activity,
float duty,
PwrActivityOrigin origin);
float activity() const { return activity_; }
float duty() const { return duty_; }
PwrActivityOrigin origin() { return origin_; }
const char *originName() const;
void set(float activity,
float duty,
PwrActivityOrigin origin);
bool isSet() const;
private:
// In general activity is per clock cycle, NOT per second.
float activity_;
float duty_;
PwrActivityOrigin origin_;
};
// The Power class has access to Sta components directly for // The Power class has access to Sta components directly for
// convenience but also requires access to the Sta class member functions. // convenience but also requires access to the Sta class member functions.
@ -40,10 +80,27 @@ public:
const Corner *corner, const Corner *corner,
// Return values. // Return values.
PowerResult &result); PowerResult &result);
float defaultSignalToggleRate(); void setGlobalActivity(float activity,
void setDefaultSignalToggleRate(float rate); float duty);
void setInputActivity(float activity,
float duty);
void setInputPortActivity(const Port *input_port,
float activity,
float duty);
PwrActivity &pinActivity(const Pin *pin);
void setPinActivity(const Pin *pin,
float activity,
float duty,
PwrActivityOrigin origin);
void setPinActivity(const Pin *pin,
PwrActivity &activity);
// Activity is toggles per second.
PwrActivity findActivity(const Pin *pin);
protected: protected:
void preamble();
void ensureActivities();
void power(const Instance *inst, void power(const Instance *inst,
LibertyCell *cell, LibertyCell *cell,
const Corner *corner, const Corner *corner,
@ -52,7 +109,7 @@ protected:
void findInternalPower(const Instance *inst, void findInternalPower(const Instance *inst,
LibertyCell *cell, LibertyCell *cell,
const LibertyPort *to_port, const LibertyPort *to_port,
float activity, PwrActivity &activity,
float load_cap, float load_cap,
const DcalcAnalysisPt *dcalc_ap, const DcalcAnalysisPt *dcalc_ap,
// Return values. // Return values.
@ -63,28 +120,35 @@ protected:
PowerResult &result); PowerResult &result);
void findSwitchingPower(LibertyCell *cell, void findSwitchingPower(LibertyCell *cell,
const LibertyPort *to_port, const LibertyPort *to_port,
float activity, PwrActivity &activity,
float load_cap, float load_cap,
const DcalcAnalysisPt *dcalc_ap, const DcalcAnalysisPt *dcalc_ap,
// Return values. // Return values.
PowerResult &result); PowerResult &result);
const Clock *findInstClk(const Instance *inst); const Clock *findInstClk(const Instance *inst);
void findClk(const Pin *to_pin, const Clock *findClk(const Pin *to_pin);
// Return values. PwrActivity findActivity(const Pin *pin,
const Clock *&clk, const Clock *inst_clk);
bool &is_clk);
void activity(const Pin *pin,
const Clock *inst_clk,
// Return values.
float &activity,
bool &is_clk);
float voltage(LibertyCell *cell, float voltage(LibertyCell *cell,
const LibertyPort *port, const LibertyPort *port,
const DcalcAnalysisPt *dcalc_ap); const DcalcAnalysisPt *dcalc_ap);
void seedActivities(BfsFwdIterator &bfs);
void seedRegOutputActivities(BfsFwdIterator &bfs);
void seedRegOutputActivities(Instance *reg,
Sequential *seq,
LibertyPort *output,
bool invert);
PwrActivity evalActivity(FuncExpr *expr,
const Instance *inst);
private: private:
Sta *sta_; PwrActivity global_activity_;
float default_signal_toggle_rate_; PwrActivity input_activity_;
float default_activity_;
PwrActivityMap activity_map_;
bool activities_valid_;
friend class PropActivityVisitor;
}; };
class PowerResult class PowerResult

View File

@ -29,7 +29,7 @@
#include "PathEnd.hh" #include "PathEnd.hh"
#include "PathExpanded.hh" #include "PathExpanded.hh"
#include "PathRef.hh" #include "PathRef.hh"
#include "Property.hh" #include "Power.hh"
#include "Sta.hh" #include "Sta.hh"
#include "Property.hh" #include "Property.hh"
@ -208,6 +208,12 @@ PropertyValue::PropertyValue(PathRefSeq *value) :
{ {
} }
PropertyValue::PropertyValue(PwrActivity *value) :
type_(type_pwr_activity),
pwr_activity_(*value)
{
}
PropertyValue::PropertyValue(const PropertyValue &value) : PropertyValue::PropertyValue(const PropertyValue &value) :
type_(value.type_) type_(value.type_)
{ {
@ -253,6 +259,9 @@ PropertyValue::PropertyValue(const PropertyValue &value) :
case Type::type_path_refs: case Type::type_path_refs:
path_refs_ = value.path_refs_ ? new PathRefSeq(*value.path_refs_) : nullptr; path_refs_ = value.path_refs_ ? new PathRefSeq(*value.path_refs_) : nullptr;
break; break;
case Type::type_pwr_activity:
pwr_activity_ = value.pwr_activity_;
break;
} }
} }
@ -299,12 +308,17 @@ PropertyValue::PropertyValue(PropertyValue &&value) :
break; break;
case Type::type_clks: case Type::type_clks:
clks_ = value.clks_; clks_ = value.clks_;
// Steal the value.
value.clks_ = nullptr; value.clks_ = nullptr;
break; break;
case Type::type_path_refs: case Type::type_path_refs:
path_refs_ = value.path_refs_; path_refs_ = value.path_refs_;
// Steal the value.
value.clks_ = nullptr; value.clks_ = nullptr;
break; break;
case Type::type_pwr_activity:
pwr_activity_ = value.pwr_activity_;
break;
} }
} }
@ -374,6 +388,9 @@ PropertyValue::operator=(const PropertyValue &value)
case Type::type_path_refs: case Type::type_path_refs:
path_refs_ = value.path_refs_ ? new PathRefSeq(*value.path_refs_) : nullptr; path_refs_ = value.path_refs_ ? new PathRefSeq(*value.path_refs_) : nullptr;
break; break;
case Type::type_pwr_activity:
pwr_activity_ = value.pwr_activity_;
break;
} }
return *this; return *this;
} }
@ -428,6 +445,9 @@ PropertyValue::operator=(PropertyValue &&value)
path_refs_ = value.path_refs_; path_refs_ = value.path_refs_;
value.clks_ = nullptr; value.clks_ = nullptr;
break; break;
case Type::type_pwr_activity:
pwr_activity_ = value.pwr_activity_;
break;
} }
return *this; return *this;
} }
@ -534,6 +554,12 @@ getProperty(const Port *port,
return PropertyValue(network->name(port)); return PropertyValue(network->name(port));
else if (stringEqual(property, "direction")) else if (stringEqual(property, "direction"))
return PropertyValue(network->direction(port)->name()); return PropertyValue(network->direction(port)->name());
else if (stringEqual(property, "activity")) {
const Instance *top_inst = network->topInstance();
const Pin *pin = network->findPin(top_inst, port);
PwrActivity activity = sta->power()->findActivity(pin);
return PropertyValue(&activity);
}
else if (stringEqual(property, "actual_fall_transition_min")) else if (stringEqual(property, "actual_fall_transition_min"))
return portSlewProperty(port, TransRiseFall::fall(), MinMax::min(), sta); return portSlewProperty(port, TransRiseFall::fall(), MinMax::min(), sta);
@ -637,6 +663,10 @@ getProperty(const Pin *pin,
sta->clocks(pin, clks); sta->clocks(pin, clks);
return PropertyValue(&clks); return PropertyValue(&clks);
} }
else if (stringEqual(property, "activity")) {
PwrActivity activity = sta->power()->findActivity(pin);
return PropertyValue(&activity);
}
else if (stringEqual(property, "max_fall_slack")) else if (stringEqual(property, "max_fall_slack"))
return pinSlackProperty(pin, TransRiseFall::fall(), MinMax::max(), sta); return pinSlackProperty(pin, TransRiseFall::fall(), MinMax::max(), sta);

View File

@ -28,6 +28,7 @@ namespace sta {
using std::string; using std::string;
class Sta; class Sta;
class PwrActivity;
class PropertyValue class PropertyValue
{ {
@ -36,7 +37,7 @@ public:
type_liberty_library, type_liberty_cell, type_liberty_library, type_liberty_cell,
type_library, type_cell, type_library, type_cell,
type_instance, type_pin, type_pins, type_net, type_instance, type_pin, type_pins, type_net,
type_clk, type_clks, type_path_refs }; type_clk, type_clks, type_path_refs, type_pwr_activity };
PropertyValue(); PropertyValue();
PropertyValue(const char *value); PropertyValue(const char *value);
PropertyValue(string &value); PropertyValue(string &value);
@ -54,6 +55,7 @@ public:
PropertyValue(ClockSeq *value); PropertyValue(ClockSeq *value);
PropertyValue(ClockSet *value); PropertyValue(ClockSet *value);
PropertyValue(PathRefSeq *value); PropertyValue(PathRefSeq *value);
PropertyValue(PwrActivity *value);
// Copy constructor. // Copy constructor.
PropertyValue(const PropertyValue &props); PropertyValue(const PropertyValue &props);
// Move constructor. // Move constructor.
@ -73,6 +75,7 @@ public:
Clock *clock() const { return clk_; } Clock *clock() const { return clk_; }
ClockSeq *clocks() const { return clks_; } ClockSeq *clocks() const { return clks_; }
PathRefSeq *pathRefs() const { return path_refs_; } PathRefSeq *pathRefs() const { return path_refs_; }
PwrActivity pwrActivity() const { return pwr_activity_; }
// Copy assignment. // Copy assignment.
PropertyValue &operator=(const PropertyValue &); PropertyValue &operator=(const PropertyValue &);
// Move assignment. // Move assignment.
@ -94,6 +97,7 @@ private:
Clock *clk_; Clock *clk_;
ClockSeq *clks_; ClockSeq *clks_;
PathRefSeq *path_refs_; PathRefSeq *path_refs_;
PwrActivity pwr_activity_;
}; };
}; };

View File

@ -33,7 +33,7 @@ proc_redirect report_power {
parse_key_args "report_power" args keys {-instances -corner -digits} flags {} 1 parse_key_args "report_power" args keys {-instances -corner -digits} flags {} 1
if [info exists keys(-digits)] { if { [info exists keys(-digits)] } {
set digits $keys(-digits) set digits $keys(-digits)
check_positive_integer "-digits" $digits check_positive_integer "-digits" $digits
} else { } else {
@ -191,22 +191,55 @@ proc report_power_inst { inst power_result field_width digits } {
################################################################ ################################################################
set ::power_default_signal_toggle_rate 0.1 define_cmd_args "set_power_activity" { [-global]\
[-input]\
[-input_ports ports]\
[-pins pins]\
[-activiity activity]\
[-duty duty] }
trace variable ::power_default_signal_toggle_rate "rw" \ proc set_power_activity { args } {
sta::trace_power_default_signal_toggle_rate parse_key_args "set_power_activity" args \
keys {-input_ports -pins -activity -duty} \
flags {-global -input} 1
proc trace_power_default_signal_toggle_rate { name1 name2 op } { set activity 0.0
global power_default_signal_toggle_rate if { [info exists keys(-activity)] } {
set activity $keys(-activity)
check_float "activity" $activity
if { $activity < 0.0 } {
sta_warn "activity should be 0.0 to 1.0 or 2.0"
}
}
set duty 0.5
if { [info exists keys(-duty)] } {
set duty $keys(-duty)
check_float "duty" $duty
if { $duty < 0.0 || $duty > 1.0 } {
sta_warn "duty should be 0.0 to 1.0"
}
}
if { $op == "r" } { if { [info exists flags(-global)] } {
set power_default_signal_toggle_rate [power_default_signal_toggle_rate] set_power_global_activity $activity $duty
} elseif { $op == "w" } { }
if { [string is double $power_default_signal_toggle_rate] \ if { [info exists flags(-input)] } {
&& $power_default_signal_toggle_rate >= 0.0 } { set_power_input_activity $activity $duty
set_power_default_signal_toggle_rate $power_default_signal_toggle_rate }
} else { if { [info exists keys(-input_ports)] } {
sta_error "power_default_signal_toggle_rate must be a positive float." set ports [get_ports_error "input_ports" $keys(-input_ports)]
foreach port $ports {
if { [get_property $port "direction"] == "input" } {
set_power_input_port_activity $port $activity $duty
}
}
}
if { [info exists keys(-pins)] } {
set ports [get_pins_error "pins" $keys(-pins)]
foreach pin $pins {
if { [get_property $pin "direction"] == "input" } {
set_power_pin_activity $pin $activity $duty
}
} }
} }
} }

View File

@ -1619,6 +1619,27 @@ using namespace sta;
Tcl_Obj *obj = SWIG_NewInstanceObj(copy, SWIGTYPE_p_PathRef, false); Tcl_Obj *obj = SWIG_NewInstanceObj(copy, SWIGTYPE_p_PathRef, false);
Tcl_ListObjAppendElement(interp, list, obj); Tcl_ListObjAppendElement(interp, list, obj);
} }
Tcl_SetObjResult(interp, list);
}
break;
case PropertyValue::Type::type_pwr_activity: {
PwrActivity activity = value.pwrActivity();
Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
Tcl_Obj *obj;
const char *str;
str = stringPrintTmp("%.5e", activity.activity());
obj = Tcl_NewStringObj(str, strlen(str));
Tcl_ListObjAppendElement(interp, list, obj);
str = stringPrintTmp("%.3f", activity.duty());
obj = Tcl_NewStringObj(str, strlen(str));
Tcl_ListObjAppendElement(interp, list, obj);
str = activity.originName();
obj = Tcl_NewStringObj(str, strlen(str));
Tcl_ListObjAppendElement(interp, list, obj);
Tcl_SetObjResult(interp, list); Tcl_SetObjResult(interp, list);
} }
break; break;
@ -4670,16 +4691,35 @@ instance_power(Instance *inst,
return floats; return floats;
} }
float void
power_default_signal_toggle_rate() set_power_global_activity(float activity,
float duty)
{ {
return Sta::sta()->power()->defaultSignalToggleRate(); Sta::sta()->power()->setGlobalActivity(activity, duty);
} }
void void
set_power_default_signal_toggle_rate(float rate) set_power_input_activity(float activity,
float duty)
{ {
return Sta::sta()->power()->setDefaultSignalToggleRate(rate); return Sta::sta()->power()->setInputActivity(activity, duty);
}
void
set_power_input_port_activity(const Port *input_port,
float activity,
float duty)
{
return Sta::sta()->power()->setInputPortActivity(input_port, activity, duty);
}
void
set_power_pin_activity(const Pin *pin,
float activity,
float duty)
{
return Sta::sta()->power()->setPinActivity(pin, activity, duty,
PwrActivityOrigin::user);
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////