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)
project(STA VERSION 2.0.14)
project(STA VERSION 2.0.15)
set(CMAKE_VERBOSE_MAKEFILE ON)
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.
Use CMake as documented in README.mb.
....
The check_timing command -no_output_delay checks output ports for
set_output_delay.
....
The report_power command reports the power consumption of the design
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,
switching, leakage and total power are reported. Design power is
reported separately for combinational, sequential, macro and pad
groups.
The internal, switching, leakage and total power are reported. Design
power is reported separately for combinational, sequential, macro and
pad 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.
@ -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
input and .measure statement for each gate delay and pin slew.
....
The report_checks, find_timing_paths commands now support an
-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
for compatibility in this current release.
....
The read_parasitics command has been renamed read_spef and no longer
supports the SPF format.

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

View File

@ -878,9 +878,6 @@ LibertyCell::~LibertyCell()
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
LibertyCell::deleteTimingArcAttrs()
{
@ -1176,6 +1173,7 @@ LibertyCell::makeTimingArcMap(Report *)
// match->from()->name(),
// match->to()->name(),
// match->role()->asString());
delete arc_set;
}
else
// Shift arc sets down to fill holes left by removed duplicates.

View File

@ -17,23 +17,30 @@
#include <algorithm> // max
#include "Machine.hh"
#include "Debug.hh"
#include "EnumNameMap.hh"
#include "Units.hh"
#include "Transition.hh"
#include "MinMax.hh"
#include "Clock.hh"
#include "TimingRole.hh"
#include "Liberty.hh"
#include "InternalPower.hh"
#include "LeakagePower.hh"
#include "Sequential.hh"
#include "TimingArc.hh"
#include "FuncExpr.hh"
#include "PortDirection.hh"
#include "Network.hh"
#include "Graph.hh"
#include "Sim.hh"
#include "Corner.hh"
#include "Sdc.hh"
#include "DcalcAnalysisPt.hh"
#include "GraphDelayCalc.hh"
#include "PathVertex.hh"
#include "Clock.hh"
#include "Levelize.hh"
#include "Sim.hh"
#include "Search.hh"
#include "Bfs.hh"
#include "Power.hh"
// Related liberty not supported:
@ -54,23 +61,65 @@ typedef Map<const char*, SumCount, CharPtrLess> SupplySumCounts;
Power::Power(Sta *sta) :
StaState(sta),
sta_(sta),
default_signal_toggle_rate_(.1)
global_activity_{0.0, 0.0, PwrActivityOrigin::unknown},
input_activity_{0.1, 0.5, PwrActivityOrigin::input},
default_activity_(.1),
activities_valid_(false)
{
}
float
Power::defaultSignalToggleRate()
{
return default_signal_toggle_rate_;
}
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
Power::power(const Corner *corner,
@ -86,6 +135,8 @@ Power::power(const Corner *corner,
combinational.clear();
macro.clear();
pad.clear();
preamble();
LeafInstanceIterator *inst_iter = network_->leafInstanceIterator();
while (inst_iter->hasNext()) {
Instance *inst = inst_iter->next();
@ -107,8 +158,6 @@ Power::power(const Corner *corner,
delete inst_iter;
}
////////////////////////////////////////////////////////////////
void
Power::power(const Instance *inst,
const Corner *corner,
@ -116,10 +165,268 @@ Power::power(const Instance *inst,
PowerResult &result)
{
LibertyCell *cell = network_->libertyCell(inst);
if (cell)
if (cell) {
preamble();
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
Power::power(const Instance *inst,
LibertyCell *cell,
@ -137,13 +444,11 @@ Power::power(const Instance *inst,
float load_cap = to_port->direction()->isAnyOutput()
? graph_delay_calc_->loadCap(to_pin, TransRiseFall::rise(), dcalc_ap)
: 0.0;
float activity1;
bool is_clk;
activity(to_pin, inst_clk, activity1, is_clk);
PwrActivity activity = findActivity(to_pin, inst_clk);
if (to_port->direction()->isAnyOutput())
findSwitchingPower(cell, to_port, activity1, load_cap,
findSwitchingPower(cell, to_port, activity, load_cap,
dcalc_ap, result);
findInternalPower(inst, cell, to_port, activity1,
findInternalPower(inst, cell, to_port, activity,
load_cap, dcalc_ap, result);
}
delete pin_iter;
@ -157,10 +462,8 @@ Power::findInstClk(const Instance *inst)
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next();
const Clock *clk = nullptr;
bool is_clk;
findClk(pin, clk, is_clk);
if (is_clk)
const Clock *clk = findClk(pin);
if (clk)
inst_clk = clk;
}
delete pin_iter;
@ -171,7 +474,7 @@ void
Power::findInternalPower(const Instance *inst,
LibertyCell *cell,
const LibertyPort *to_port,
float activity,
PwrActivity &activity,
float load_cap,
const DcalcAnalysisPt *dcalc_ap,
// Return values.
@ -183,10 +486,9 @@ Power::findInternalPower(const Instance *inst,
cell->name());
SupplySumCounts supply_sum_counts;
const Pvt *pvt = dcalc_ap->operatingConditions();
float duty = 1.0;
debugPrint2(debug_, "power", 2, " cap = %s duty = %.1f\n",
units_->capacitanceUnit()->asString(load_cap),
duty);
debugPrint1(debug_, "power", 2, " cap = %s\n",
units_->capacitanceUnit()->asString(load_cap));
debugPrint0(debug_, "power", 2, " when act/ns duty energy power\n");
LibertyCellInternalPowerIterator pwr_iter(cell);
while (pwr_iter.hasNext()) {
InternalPower *pwr = pwr_iter.next();
@ -197,33 +499,37 @@ Power::findInternalPower(const Instance *inst,
from_port = to_port;
const Pin *from_pin = network_->findPin(inst, from_port);
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;
TransRiseFallIterator tr_iter;
while (tr_iter.hasNext()) {
TransRiseFall *to_tr = tr_iter.next();
// Should use unateness to find from_tr.
TransRiseFall *from_tr = to_tr;
float slew = delayAsFloat(sta_->vertexSlew(from_vertex,
from_tr, dcalc_ap));
float energy;
if (from_port)
energy = pwr->power(to_tr, pvt, slew, load_cap);
else
energy = pwr->power(to_tr, pvt, 0.0, 0.0);
float tr_internal = energy * activity * duty;
float slew = delayAsFloat(graph_->slew(from_vertex,
from_tr,
dcalc_ap->index()));
float tr_energy = (from_port)
? pwr->power(to_tr, pvt, slew, load_cap)
: pwr->power(to_tr, pvt, 0.0, 0.0);
float tr_internal = tr_energy * activity.activity();
port_energy += tr_energy;
port_internal += tr_internal;
debugPrint5(debug_, "power", 2, " %s -> %s %s %s (%s)\n",
from_port->name(),
to_tr->shortName(),
to_port->name(),
pwr->when() ? pwr->when()->asString() : "",
related_pg_pin ? related_pg_pin : "(no pg_pin)");
debugPrint4(debug_, "power", 2, " slew = %s activity = %.2f/ns energy = %.5g pwr = %.5g\n",
units_->timeUnit()->asString(slew),
activity * 1e-9,
energy,
tr_internal);
}
debugPrint8(debug_, "power", 2, " %s -> %s %s %.2f %.2f %9.2e %9.2e %s\n",
from_port->name(),
to_port->name(),
pwr->when() ? pwr->when()->asString() : " ",
activity.activity() * 1e-9,
duty,
port_energy,
port_internal,
related_pg_pin ? related_pg_pin : "no pg_pin");
// Sum/count internal power arcs by supply to average across conditions.
SumCount &supply_sum_count = supply_sum_counts[related_pg_pin];
// Average rise/fall internal power.
@ -239,7 +545,9 @@ Power::findInternalPower(const Instance *inst,
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);
}
@ -303,43 +611,62 @@ Power::findLeakagePower(const Instance *,
void
Power::findSwitchingPower(LibertyCell *cell,
const LibertyPort *to_port,
float activity,
PwrActivity &activity,
float load_cap,
const DcalcAnalysisPt *dcalc_ap,
// Return values.
PowerResult &result)
{
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",
cell->name(),
to_port->name(),
activity,
activity.activity(),
volt,
switching);
volt = voltage(cell, to_port, dcalc_ap);
result.setSwitching(result.switching() + switching);
}
void
Power::activity(const Pin *pin,
const Clock *inst_clk,
// Return values.
float &activity,
bool &is_clk)
PwrActivity
Power::findActivity(const Pin *pin)
{
const Clock *clk = inst_clk;
findClk(pin, clk, is_clk);
activity = 0.0;
const Instance *inst = network_->instance(pin);
const Clock *inst_clk = findInstClk(inst);
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) {
float period = clk->period();
if (period > 0.0) {
if (is_clk)
activity = 2.0 / period;
else
activity = default_signal_toggle_rate_ / period;
Vertex *vertex = graph_->pinLoadVertex(pin);
if (search_->isClock(vertex))
return PwrActivity(2.0 / period, 0.5, PwrActivityOrigin::clock);
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
@ -370,13 +697,10 @@ Power::voltage(LibertyCell *cell,
return 0.0;
}
void
Power::findClk(const Pin *to_pin,
// Return values.
const Clock *&clk,
bool &is_clk)
const Clock *
Power::findClk(const Pin *to_pin)
{
is_clk = false;
const Clock *clk = nullptr;
Vertex *to_vertex = graph_->pinDrvrVertex(to_pin);
VertexPathIterator path_iter(to_vertex, this);
while (path_iter.hasNext()) {
@ -386,9 +710,8 @@ Power::findClk(const Pin *to_pin,
&& (clk == nullptr
|| path_clk->period() < clk->period()))
clk = path_clk;
if (path->isClock(this))
is_clk = true;
}
return clk;
}
////////////////////////////////////////////////////////////////
@ -440,4 +763,54 @@ PowerResult::incr(PowerResult &result)
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

View File

@ -22,6 +22,46 @@
namespace sta {
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
// convenience but also requires access to the Sta class member functions.
@ -40,10 +80,27 @@ public:
const Corner *corner,
// Return values.
PowerResult &result);
float defaultSignalToggleRate();
void setDefaultSignalToggleRate(float rate);
void setGlobalActivity(float activity,
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:
void preamble();
void ensureActivities();
void power(const Instance *inst,
LibertyCell *cell,
const Corner *corner,
@ -52,7 +109,7 @@ protected:
void findInternalPower(const Instance *inst,
LibertyCell *cell,
const LibertyPort *to_port,
float activity,
PwrActivity &activity,
float load_cap,
const DcalcAnalysisPt *dcalc_ap,
// Return values.
@ -63,28 +120,35 @@ protected:
PowerResult &result);
void findSwitchingPower(LibertyCell *cell,
const LibertyPort *to_port,
float activity,
PwrActivity &activity,
float load_cap,
const DcalcAnalysisPt *dcalc_ap,
// Return values.
PowerResult &result);
const Clock *findInstClk(const Instance *inst);
void findClk(const Pin *to_pin,
// Return values.
const Clock *&clk,
bool &is_clk);
void activity(const Pin *pin,
const Clock *inst_clk,
// Return values.
float &activity,
bool &is_clk);
const Clock *findClk(const Pin *to_pin);
PwrActivity findActivity(const Pin *pin,
const Clock *inst_clk);
float voltage(LibertyCell *cell,
const LibertyPort *port,
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:
Sta *sta_;
float default_signal_toggle_rate_;
PwrActivity global_activity_;
PwrActivity input_activity_;
float default_activity_;
PwrActivityMap activity_map_;
bool activities_valid_;
friend class PropActivityVisitor;
};
class PowerResult

View File

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

View File

@ -28,6 +28,7 @@ namespace sta {
using std::string;
class Sta;
class PwrActivity;
class PropertyValue
{
@ -36,7 +37,7 @@ public:
type_liberty_library, type_liberty_cell,
type_library, type_cell,
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(const char *value);
PropertyValue(string &value);
@ -54,6 +55,7 @@ public:
PropertyValue(ClockSeq *value);
PropertyValue(ClockSet *value);
PropertyValue(PathRefSeq *value);
PropertyValue(PwrActivity *value);
// Copy constructor.
PropertyValue(const PropertyValue &props);
// Move constructor.
@ -73,6 +75,7 @@ public:
Clock *clock() const { return clk_; }
ClockSeq *clocks() const { return clks_; }
PathRefSeq *pathRefs() const { return path_refs_; }
PwrActivity pwrActivity() const { return pwr_activity_; }
// Copy assignment.
PropertyValue &operator=(const PropertyValue &);
// Move assignment.
@ -94,6 +97,7 @@ private:
Clock *clk_;
ClockSeq *clks_;
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
if [info exists keys(-digits)] {
if { [info exists keys(-digits)] } {
set digits $keys(-digits)
check_positive_integer "-digits" $digits
} 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" \
sta::trace_power_default_signal_toggle_rate
proc set_power_activity { args } {
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 } {
global power_default_signal_toggle_rate
set activity 0.0
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" } {
set power_default_signal_toggle_rate [power_default_signal_toggle_rate]
} elseif { $op == "w" } {
if { [string is double $power_default_signal_toggle_rate] \
&& $power_default_signal_toggle_rate >= 0.0 } {
set_power_default_signal_toggle_rate $power_default_signal_toggle_rate
} else {
sta_error "power_default_signal_toggle_rate must be a positive float."
if { [info exists flags(-global)] } {
set_power_global_activity $activity $duty
}
if { [info exists flags(-input)] } {
set_power_input_activity $activity $duty
}
if { [info exists keys(-input_ports)] } {
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_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);
}
break;
@ -4670,16 +4691,35 @@ instance_power(Instance *inst,
return floats;
}
float
power_default_signal_toggle_rate()
void
set_power_global_activity(float activity,
float duty)
{
return Sta::sta()->power()->defaultSignalToggleRate();
Sta::sta()->power()->setGlobalActivity(activity, duty);
}
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);
}
////////////////////////////////////////////////////////////////