OpenSTA/search/Power.cc

1213 lines
33 KiB
C++
Raw Normal View History

2019-06-20 01:05:13 +02:00
// OpenSTA, Static Timing Analyzer
2020-03-07 03:50:37 +01:00
// Copyright (c) 2020, Parallax Software, Inc.
2018-11-26 18:15:52 +01:00
//
2019-06-20 01:05:13 +02:00
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
2018-11-26 18:15:52 +01:00
2020-04-05 23:53:44 +02:00
#include "Power.hh"
2020-04-05 20:35:51 +02:00
2018-11-26 18:15:52 +01:00
#include <algorithm> // max
2020-12-26 17:29:43 +01:00
#include <cmath> // aps
2020-04-05 20:35:51 +02:00
2020-04-05 23:53:44 +02:00
#include "Debug.hh"
#include "EnumNameMap.hh"
2020-07-30 16:55:48 +02:00
#include "Hash.hh"
2020-04-05 23:53:44 +02:00
#include "MinMax.hh"
#include "Units.hh"
#include "Transition.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 "Clock.hh"
#include "Sdc.hh"
#include "Graph.hh"
#include "DcalcAnalysisPt.hh"
#include "GraphDelayCalc.hh"
#include "Corner.hh"
#include "PathVertex.hh"
#include "Levelize.hh"
#include "Sim.hh"
#include "Search.hh"
#include "Bfs.hh"
2018-11-26 18:15:52 +01:00
// Related liberty not supported:
// library
// default_cell_leakage_power : 0;
// output_voltage (default_VDD_VSS_output) {
// leakage_power
// related_pg_pin : VDD;
// internal_power
// input_voltage : default_VDD_VSS_input;
// pin
// output_voltage : default_VDD_VSS_output;
2020-04-29 05:32:59 +02:00
//
// transition_density = activity / clock_period
2018-11-26 18:15:52 +01:00
namespace sta {
2020-12-26 17:29:43 +01:00
using std::abs;
2020-04-29 05:32:59 +02:00
static bool
isPositiveUnate(const LibertyCell *cell,
const LibertyPort *from,
const LibertyPort *to);
2019-02-18 19:56:38 +01:00
2020-07-31 18:42:24 +02:00
Power::Power(StaState *sta) :
2018-11-26 18:15:52 +01:00
StaState(sta),
2019-04-29 17:39:05 +02:00
global_activity_{0.0, 0.0, PwrActivityOrigin::unknown},
input_activity_{0.1, 0.5, PwrActivityOrigin::input},
activities_valid_(false)
2018-11-26 18:15:52 +01:00
{
}
2019-04-29 17:39:05 +02:00
void
Power::setGlobalActivity(float activity,
float duty)
{
global_activity_.set(activity, duty, PwrActivityOrigin::global);
activities_valid_ = false;
}
void
Power::setInputActivity(float activity,
float duty)
2018-11-26 18:15:52 +01:00
{
2019-04-29 17:39:05 +02:00
input_activity_.set(activity, duty, PwrActivityOrigin::input);
activities_valid_ = false;
2018-11-26 18:15:52 +01:00
}
void
2019-04-29 17:39:05 +02:00
Power::setInputPortActivity(const Port *input_port,
float activity,
float duty)
2018-11-26 18:15:52 +01:00
{
2019-04-29 17:39:05 +02:00
Instance *top_inst = network_->topInstance();
const Pin *pin = network_->findPin(top_inst, input_port);
if (pin) {
2020-08-05 16:23:38 +02:00
user_activity_map_[pin] = {activity, duty, PwrActivityOrigin::user};
2019-04-29 17:39:05 +02:00
activities_valid_ = false;
}
2018-11-26 18:15:52 +01:00
}
2020-08-05 16:23:38 +02:00
void
Power::setUserActivity(const Pin *pin,
float activity,
float duty,
PwrActivityOrigin origin)
{
user_activity_map_[pin] = {activity, duty, origin};
activities_valid_ = false;
}
2019-04-29 17:39:05 +02:00
PwrActivity &
2020-08-05 16:23:38 +02:00
Power::userActivity(const Pin *pin)
2019-04-29 17:39:05 +02:00
{
2020-08-05 16:23:38 +02:00
return user_activity_map_[pin];
2019-04-29 17:39:05 +02:00
}
2019-05-01 03:17:36 +02:00
bool
2020-08-05 16:23:38 +02:00
Power::hasUserActivity(const Pin *pin)
2019-05-01 03:17:36 +02:00
{
2020-08-05 16:23:38 +02:00
return user_activity_map_.hasKey(pin);
2019-05-01 03:17:36 +02:00
}
2019-04-29 17:39:05 +02:00
void
2020-08-05 16:23:38 +02:00
Power::setActivity(const Pin *pin,
PwrActivity &activity)
2019-04-29 17:39:05 +02:00
{
activity_map_[pin] = activity;
2019-04-29 17:39:05 +02:00
}
2020-08-05 16:23:38 +02:00
PwrActivity &
Power::activity(const Pin *pin)
2019-04-29 17:39:05 +02:00
{
2020-08-05 16:23:38 +02:00
return activity_map_[pin];
}
bool
Power::hasActivity(const Pin *pin)
{
return activity_map_.hasKey(pin);
2019-04-29 17:39:05 +02:00
}
2018-11-26 18:15:52 +01:00
2020-07-30 16:55:48 +02:00
// Sequential internal pins may not be in the netlist so their
// activities are stored by instance/liberty_port pairs.
void
Power::setSeqActivity(const Instance *reg,
LibertyPort *output,
PwrActivity &activity)
{
seq_activity_map_[SeqPin(reg, output)] = activity;
activities_valid_ = false;
}
bool
Power::hasSeqActivity(const Instance *reg,
LibertyPort *output)
{
return seq_activity_map_.hasKey(SeqPin(reg, output));
}
PwrActivity
Power::seqActivity(const Instance *reg,
LibertyPort *output)
{
return seq_activity_map_[SeqPin(reg, output)];
}
size_t
SeqPinHash::operator()(const SeqPin &pin) const
{
return hashSum(hashPtr(pin.first), hashPtr(pin.second));
}
bool
SeqPinEqual::operator()(const SeqPin &pin1,
const SeqPin &pin2) const
{
return pin1.first == pin2.first
&& pin1.second == pin2.second;
}
////////////////////////////////////////////////////////////////
2018-11-26 18:15:52 +01:00
void
Power::power(const Corner *corner,
// Return values.
PowerResult &total,
PowerResult &sequential,
PowerResult &combinational,
PowerResult &macro,
PowerResult &pad)
{
total.clear();
sequential.clear();
combinational.clear();
macro.clear();
pad.clear();
2019-04-29 17:39:05 +02:00
preamble();
2018-11-26 18:15:52 +01:00
LeafInstanceIterator *inst_iter = network_->leafInstanceIterator();
while (inst_iter->hasNext()) {
Instance *inst = inst_iter->next();
LibertyCell *cell = network_->libertyCell(inst);
if (cell) {
PowerResult inst_power;
2020-07-30 03:14:56 +02:00
power(inst, cell, corner, inst_power);
2020-06-10 05:02:59 +02:00
if (cell->isMacro()
|| cell->isMemory())
2018-11-26 18:15:52 +01:00
macro.incr(inst_power);
else if (cell->isPad())
pad.incr(inst_power);
else if (cell->hasSequentials())
sequential.incr(inst_power);
else
combinational.incr(inst_power);
total.incr(inst_power);
}
}
2018-12-05 23:18:41 +01:00
delete inst_iter;
2018-11-26 18:15:52 +01:00
}
void
Power::power(const Instance *inst,
const Corner *corner,
// Return values.
PowerResult &result)
{
LibertyCell *cell = network_->libertyCell(inst);
2019-04-29 17:39:05 +02:00
if (cell) {
preamble();
2018-11-26 18:15:52 +01:00
power(inst, cell, corner, result);
2019-04-29 17:39:05 +02:00
}
}
////////////////////////////////////////////////////////////////
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);
2019-05-01 03:17:36 +02:00
~PropActivityVisitor();
2019-04-29 17:39:05 +02:00
virtual VertexVisitor *copy();
virtual void visit(Vertex *vertex);
2019-05-01 03:17:36 +02:00
void init();
bool foundRegWithoutActivity() const;
InstanceSet *stealVisitedRegs();
2019-04-29 17:39:05 +02:00
private:
2019-05-01 03:17:36 +02:00
InstanceSet *visited_regs_;
bool found_reg_without_activity_;
2019-04-29 17:39:05 +02:00
Power *power_;
BfsFwdIterator *bfs_;
};
PropActivityVisitor::PropActivityVisitor(Power *power,
BfsFwdIterator *bfs) :
StaState(power),
2019-05-01 03:17:36 +02:00
visited_regs_(nullptr),
2019-04-29 17:39:05 +02:00
power_(power),
bfs_(bfs)
{
}
2019-05-01 03:17:36 +02:00
PropActivityVisitor::~PropActivityVisitor()
{
delete visited_regs_;
}
2019-04-29 17:39:05 +02:00
VertexVisitor *
PropActivityVisitor::copy()
{
return new PropActivityVisitor(power_, bfs_);
}
2019-05-01 03:17:36 +02:00
void
PropActivityVisitor::init()
{
visited_regs_ = new InstanceSet;
found_reg_without_activity_ = false;
}
InstanceSet *
PropActivityVisitor::stealVisitedRegs()
{
InstanceSet *visited_regs = visited_regs_;
visited_regs_ = nullptr;
return visited_regs;
}
bool
PropActivityVisitor::foundRegWithoutActivity() const
{
return found_reg_without_activity_;
}
void
PropActivityVisitor::visit(Vertex *vertex)
{
2020-11-17 18:59:19 +01:00
Pin *pin = vertex->pin();
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power_activity", 3, "visit %s",
vertex->name(network_));
2020-08-05 16:23:38 +02:00
if (power_->hasUserActivity(pin))
power_->setActivity(pin, power_->userActivity(pin));
else {
bool input_without_activity = false;
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_->activity(from_pin);
PwrActivity to_activity(from_activity.activity(),
from_activity.duty(),
PwrActivityOrigin::propagated);
if (!power_->hasActivity(pin))
input_without_activity = true;
power_->setActivity(pin, to_activity);
}
}
Instance *inst = network_->instance(pin);
auto cell = network_->libertyCell(inst);
if (cell && cell->hasSequentials()) {
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power_activity", 3, "pending reg %s",
network_->pathName(inst));
2020-08-05 16:23:38 +02:00
visited_regs_->insert(inst);
found_reg_without_activity_ = input_without_activity;
2019-05-01 03:17:36 +02:00
}
}
2020-08-05 16:23:38 +02:00
if (network_->isDriver(pin)) {
LibertyPort *port = network_->libertyPort(pin);
if (port) {
FuncExpr *func = port->function();
if (func) {
Instance *inst = network_->instance(pin);
PwrActivity activity = power_->evalActivity(func, inst);
power_->setActivity(pin, activity);
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power_activity", 3, "set %s %.2e %.2f",
vertex->name(network_),
activity.activity(),
activity.duty());
2020-08-05 16:23:38 +02:00
}
}
2019-05-01 03:17:36 +02:00
}
}
bfs_->enqueueAdjacentVertices(vertex);
}
2019-04-29 17:39:05 +02:00
PwrActivity
Power::evalActivity(FuncExpr *expr,
const Instance *inst)
{
return evalActivity(expr, inst, nullptr, true);
}
// Eval activity thru expr.
// With cofactor_port eval the positive/negative cofactor of expr wrt cofactor_port.
PwrActivity
Power::evalActivity(FuncExpr *expr,
const Instance *inst,
const LibertyPort *cofactor_port,
bool cofactor_positive)
2019-04-29 17:39:05 +02:00
{
switch (expr->op()) {
case FuncExpr::op_port: {
LibertyPort *port = expr->port();
if (port == cofactor_port) {
if (cofactor_positive)
return PwrActivity(0.0, 1.0, PwrActivityOrigin::constant);
else
return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant);
}
2020-07-30 16:55:48 +02:00
if (port->direction()->isInternal())
return findSeqActivity(inst, port);
else {
2020-11-12 17:42:38 +01:00
Pin *pin = findLinkPin(inst, port);
2020-07-30 16:55:48 +02:00
if (pin)
return findActivity(pin);
}
return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant);
2019-04-29 17:39:05 +02:00
}
case FuncExpr::op_not: {
PwrActivity activity1 = evalActivity(expr->left(), inst,
cofactor_port, cofactor_positive);
2019-04-29 17:39:05 +02:00
return PwrActivity(activity1.activity(),
1.0 - activity1.duty(),
PwrActivityOrigin::propagated);
}
case FuncExpr::op_or: {
PwrActivity activity1 = evalActivity(expr->left(), inst,
cofactor_port, cofactor_positive);
PwrActivity activity2 = evalActivity(expr->right(), inst,
cofactor_port, cofactor_positive);
2019-04-29 17:39:05 +02:00
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,
cofactor_port, cofactor_positive);
PwrActivity activity2 = evalActivity(expr->right(), inst,
cofactor_port, cofactor_positive);
2019-04-29 17:39:05 +02:00
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,
cofactor_port, cofactor_positive);
PwrActivity activity2 = evalActivity(expr->right(), inst,
cofactor_port, cofactor_positive);
2019-04-29 17:39:05 +02:00
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
Power::preamble()
{
ensureActivities();
}
void
Power::ensureActivities()
{
2020-08-05 16:23:38 +02:00
// No need to propagate activites if global activity is set.
2019-04-29 17:39:05 +02:00
if (!global_activity_.isSet()) {
if (!activities_valid_) {
2020-08-05 16:23:38 +02:00
// Clear existing activities.
activity_map_.clear();
seq_activity_map_.clear();
2019-04-29 17:39:05 +02:00
ActivitySrchPred activity_srch_pred(this);
BfsFwdIterator bfs(BfsIndex::other, &activity_srch_pred, this);
seedActivities(bfs);
PropActivityVisitor visitor(this, &bfs);
2019-05-01 03:17:36 +02:00
visitor.init();
2020-08-05 16:23:38 +02:00
// Propagate activities through combinational logic.
2019-04-29 17:39:05 +02:00
bfs.visit(levelize_->maxLevel(), &visitor);
2020-08-05 16:23:38 +02:00
// Propagate activiities through registers.
2019-05-01 03:17:36 +02:00
while (visitor.foundRegWithoutActivity()) {
InstanceSet *regs = visitor.stealVisitedRegs();
InstanceSet::Iterator reg_iter(regs);
while (reg_iter.hasNext()) {
auto reg = reg_iter.next();
// Propagate activiities across register D->Q.
seedRegOutputActivities(reg, bfs);
}
2019-05-20 01:06:06 +02:00
delete regs;
2020-08-05 16:23:38 +02:00
2019-05-01 03:17:36 +02:00
visitor.init();
2020-08-05 16:23:38 +02:00
// Propagate register output activities through
// combinational logic.
2019-05-01 03:17:36 +02:00
bfs.visit(levelize_->maxLevel(), &visitor);
}
2019-04-29 17:39:05 +02:00
activities_valid_ = true;
}
}
2018-11-26 18:15:52 +01:00
}
2019-04-29 17:39:05 +02:00
void
Power::seedActivities(BfsFwdIterator &bfs)
{
for (auto vertex : levelize_->roots()) {
const Pin *pin = vertex->pin();
// Clock activities are baked in.
2019-10-25 17:51:59 +02:00
if (!sdc_->isLeafPinClock(pin)
2020-07-30 16:55:48 +02:00
&& !network_->direction(pin)->isInternal()) {
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power_activity", 3, "seed %s",
vertex->name(network_));
2020-08-05 16:23:38 +02:00
if (hasUserActivity(pin))
setActivity(pin, userActivity(pin));
else
// Default inputs without explicit activities to the input default.
setActivity(pin, input_activity_);
Vertex *vertex = graph_->pinDrvrVertex(pin);
bfs.enqueueAdjacentVertices(vertex);
2019-04-29 17:39:05 +02:00
}
}
}
void
2019-05-01 03:17:36 +02:00
Power::seedRegOutputActivities(const Instance *inst,
BfsFwdIterator &bfs)
2019-04-29 17:39:05 +02:00
{
2019-05-01 03:17:36 +02:00
auto cell = network_->libertyCell(inst);
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()) {
2020-07-30 16:55:48 +02:00
Pin *pin = pin_iter->next();
LibertyPort *port = network_->libertyPort(pin);
FuncExpr *func = port->function();
2019-05-01 03:17:36 +02:00
if (func) {
Vertex *vertex = graph_->pinDrvrVertex(pin);
if (func->port() == seq->output()
|| func->port() == seq->outputInv()) {
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power_activity", 3, "enqueue reg output %s",
vertex->name(network_));
2019-05-01 03:17:36 +02:00
bfs.enqueue(vertex);
2019-04-29 17:39:05 +02:00
}
}
}
2019-05-01 03:17:36 +02:00
delete pin_iter;
2019-04-29 17:39:05 +02:00
}
}
void
2019-05-01 03:17:36 +02:00
Power::seedRegOutputActivities(const Instance *reg,
2019-04-29 17:39:05 +02:00
Sequential *seq,
LibertyPort *output,
bool invert)
{
2020-07-30 16:55:48 +02:00
PwrActivity activity = evalActivity(seq->data(), reg);
if (invert)
activity.set(activity.activity(),
1.0 - activity.duty(),
activity.origin());
setSeqActivity(reg, output, activity);
2019-04-29 17:39:05 +02:00
}
////////////////////////////////////////////////////////////////
2018-11-26 18:15:52 +01:00
void
Power::power(const Instance *inst,
LibertyCell *cell,
const Corner *corner,
// Return values.
PowerResult &result)
{
MinMax *mm = MinMax::max();
const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm);
const Clock *inst_clk = findInstClk(inst);
2018-11-26 18:15:52 +01:00
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
const Pin *to_pin = pin_iter->next();
const LibertyPort *to_port = network_->libertyPort(to_pin);
float load_cap = to_port->direction()->isAnyOutput()
2019-05-28 07:46:24 +02:00
? graph_delay_calc_->loadCap(to_pin, dcalc_ap)
2018-11-26 18:15:52 +01:00
: 0.0;
PwrActivity activity = findClkedActivity(to_pin, inst_clk);
2020-04-29 05:32:59 +02:00
if (to_port->direction()->isAnyOutput()) {
2020-11-11 19:31:47 +01:00
findSwitchingPower(cell, to_port, activity, load_cap, corner, result);
2020-04-29 05:32:59 +02:00
findOutputInternalPower(to_pin, to_port, inst, cell, activity,
2020-11-11 19:31:47 +01:00
load_cap, corner, result);
2020-04-29 05:32:59 +02:00
}
if (to_port->direction()->isAnyInput())
findInputInternalPower(to_pin, to_port, inst, cell, activity,
2020-11-11 19:31:47 +01:00
load_cap, corner, result);
2018-11-26 18:15:52 +01:00
}
delete pin_iter;
2020-11-11 19:31:47 +01:00
findLeakagePower(inst, cell, corner, result);
2018-11-26 18:15:52 +01:00
}
const Clock *
Power::findInstClk(const Instance *inst)
{
const Clock *inst_clk = nullptr;
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next();
2019-04-29 17:39:05 +02:00
const Clock *clk = findClk(pin);
if (clk)
inst_clk = clk;
}
delete pin_iter;
return inst_clk;
}
2018-11-26 18:15:52 +01:00
void
2020-05-02 03:40:50 +02:00
Power::findInputInternalPower(const Pin *pin,
const LibertyPort *port,
2020-04-29 05:32:59 +02:00
const Instance *inst,
LibertyCell *cell,
2020-05-02 03:40:50 +02:00
PwrActivity &activity,
2020-04-29 05:32:59 +02:00
float load_cap,
2020-11-11 19:31:47 +01:00
const Corner *corner,
2020-04-29 05:32:59 +02:00
// Return values.
PowerResult &result)
{
2020-11-11 19:31:47 +01:00
int lib_ap_index = corner->libertyIndex(MinMax::max());
LibertyCell *corner_cell = cell->cornerCell(lib_ap_index);
const LibertyPort *corner_port = port->cornerPort(lib_ap_index);
auto internal_pwrs = corner_cell->internalPowers(corner_port);
2020-04-29 05:32:59 +02:00
if (!internal_pwrs->empty()) {
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power", 2, "internal input %s/%s (%s)",
network_->pathName(inst),
port->name(),
corner_cell->name());
2020-11-11 19:31:47 +01:00
const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max());
2020-04-29 05:32:59 +02:00
const Pvt *pvt = dcalc_ap->operatingConditions();
2020-05-02 03:40:50 +02:00
Vertex *vertex = graph_->pinLoadVertex(pin);
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power", 2, " cap = %s",
units_->capacitanceUnit()->asString(load_cap));
2021-01-23 00:07:04 +01:00
debugPrint0(debug_, "power", 2, " when act/ns duty energy power");
2020-04-29 05:32:59 +02:00
float internal = 0.0;
for (InternalPower *pwr : *internal_pwrs) {
const char *related_pg_pin = pwr->relatedPgPin();
2020-05-02 03:40:50 +02:00
float energy = 0.0;
int tr_count = 0;
for (auto rf : RiseFall::range()) {
float slew = delayAsFloat(graph_->slew(vertex, rf,
2020-04-29 05:32:59 +02:00
dcalc_ap->index()));
2020-07-12 01:24:48 +02:00
if (!delayInf(slew)) {
2020-05-02 03:40:50 +02:00
float table_energy = pwr->power(rf, pvt, slew, load_cap);
energy += table_energy;
tr_count++;
2020-04-29 05:32:59 +02:00
}
}
2020-05-02 03:40:50 +02:00
if (tr_count)
energy /= tr_count; // average non-inf energies
2020-05-02 06:58:25 +02:00
float duty = .5; // fallback default
2020-05-02 03:40:50 +02:00
FuncExpr *when = pwr->when();
if (when) {
2020-11-11 19:31:47 +01:00
LibertyPort *out_corner_port = findExprOutPort(when);
if (out_corner_port) {
2020-11-12 17:42:38 +01:00
const LibertyPort *out_port = findLinkPort(cell, out_corner_port);
2020-05-02 03:40:50 +02:00
FuncExpr *func = out_port->function();
2020-05-06 16:00:30 +02:00
if (func && func->hasPort(port))
duty = evalActivityDifference(func, inst, port).duty();
2020-05-02 03:40:50 +02:00
else
duty = evalActivity(when, inst).duty();
}
2020-05-02 06:58:25 +02:00
else
duty = evalActivity(when, inst).duty();
2020-05-02 03:40:50 +02:00
}
float port_internal = energy * duty * activity.activity();
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power", 2, " %3s %6s %.2f %.2f %9.2e %9.2e %s",
port->name(),
when ? when->asString() : "",
activity.activity() * 1e-9,
duty,
energy,
port_internal,
related_pg_pin ? related_pg_pin : "no pg_pin");
2020-04-29 05:32:59 +02:00
internal += port_internal;
}
2020-05-02 16:48:48 +02:00
result.internal() += internal;
2020-04-29 05:32:59 +02:00
}
}
2020-05-02 03:40:50 +02:00
LibertyPort *
Power::findExprOutPort(FuncExpr *expr)
{
LibertyPort *port;
switch (expr->op()) {
case FuncExpr::op_port:
port = expr->port();
if (port->direction()->isAnyOutput())
return expr->port();
return nullptr;
case FuncExpr::op_not:
port = findExprOutPort(expr->left());
if (port)
return port;
return nullptr;
case FuncExpr::op_or:
case FuncExpr::op_and:
case FuncExpr::op_xor:
port = findExprOutPort(expr->left());
if (port)
return port;
port = findExprOutPort(expr->right());
if (port)
return port;
return nullptr;
case FuncExpr::op_one:
case FuncExpr::op_zero:
return nullptr;
}
return nullptr;
2020-05-02 03:40:50 +02:00
}
// Eval activity of differenc(expr) wrt cofactor port.
PwrActivity
Power::evalActivityDifference(FuncExpr *expr,
const Instance *inst,
const LibertyPort *cofactor_port)
{
// Activity of positive/negative cofactors.
PwrActivity pos = evalActivity(expr, inst, cofactor_port, true);
PwrActivity neg = evalActivity(expr, inst, cofactor_port, false);
// difference = xor(pos, neg).
float p1 = pos.duty() * (1.0 - neg.duty());
float p2 = neg.duty() * (1.0 - pos.duty());
return PwrActivity(pos.activity() * p1 + neg.activity() * p2,
p1 + p2,
PwrActivityOrigin::propagated);
}
2020-04-29 05:32:59 +02:00
void
Power::findOutputInternalPower(const Pin *to_pin,
const LibertyPort *to_port,
const Instance *inst,
LibertyCell *cell,
PwrActivity &to_activity,
float load_cap,
2020-11-11 19:31:47 +01:00
const Corner *corner,
2020-04-29 05:32:59 +02:00
// Return values.
PowerResult &result)
{
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power", 2, "internal output %s/%s (%s)",
network_->pathName(inst),
to_port->name(),
cell->name());
2020-11-11 19:31:47 +01:00
const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max());
2018-11-26 18:15:52 +01:00
const Pvt *pvt = dcalc_ap->operatingConditions();
2020-11-11 19:31:47 +01:00
int lib_ap_index = corner->libertyIndex(MinMax::max());
LibertyCell *corner_cell = cell->cornerCell(lib_ap_index);
const LibertyPort *to_corner_port = to_port->cornerPort(lib_ap_index);
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power", 2, " cap = %s",
units_->capacitanceUnit()->asString(load_cap));
2020-05-02 06:58:25 +02:00
FuncExpr *func = to_port->function();
2020-05-01 18:40:26 +02:00
map<const char*, float, StringLessIf> pg_duty_sum;
2020-11-11 19:31:47 +01:00
for (InternalPower *pwr : *corner_cell->internalPowers(to_corner_port)) {
float duty = findInputDuty(to_pin, inst, func, pwr);
2020-05-02 06:58:25 +02:00
const char *related_pg_pin = pwr->relatedPgPin();
2020-05-08 02:23:38 +02:00
// Note related_pg_pin may be null.
pg_duty_sum[related_pg_pin] += duty;
2020-04-29 05:32:59 +02:00
}
float internal = 0.0;
2020-11-11 19:31:47 +01:00
for (InternalPower *pwr : *corner_cell->internalPowers(to_corner_port)) {
2020-05-02 06:58:25 +02:00
FuncExpr *when = pwr->when();
2020-04-29 05:32:59 +02:00
const char *related_pg_pin = pwr->relatedPgPin();
float duty = findInputDuty(to_pin, inst, func, pwr);
Vertex *from_vertex = nullptr;
2020-11-11 19:31:47 +01:00
bool positive_unate = true;
const LibertyPort *from_corner_port = pwr->relatedPort();
if (from_corner_port) {
2020-11-12 17:42:38 +01:00
positive_unate = isPositiveUnate(corner_cell, from_corner_port, to_corner_port);
const Pin *from_pin = findLinkPin(inst, from_corner_port);
2020-11-11 19:31:47 +01:00
if (from_pin) {
from_vertex = graph_->pinLoadVertex(from_pin);
2020-11-11 19:31:47 +01:00
}
}
2020-05-01 18:40:26 +02:00
float energy = 0.0;
int tr_count = 0;
2021-01-23 00:07:04 +01:00
debugPrint0(debug_, "power", 2,
" when act/ns duty wgt energy power");
2020-05-01 18:40:26 +02:00
for (auto to_rf : RiseFall::range()) {
// Use unateness to find from_rf.
2020-11-11 19:31:47 +01:00
RiseFall *from_rf = positive_unate ? to_rf : to_rf->opposite();
float slew = from_vertex
? delayAsFloat(graph_->slew(from_vertex, from_rf,
dcalc_ap->index()))
: 0.0;
2020-07-12 01:24:48 +02:00
if (!delayInf(slew)) {
2020-05-01 18:40:26 +02:00
float table_energy = pwr->power(to_rf, pvt, slew, load_cap);
energy += table_energy;
tr_count++;
2018-11-26 18:15:52 +01:00
}
}
2020-05-02 03:40:50 +02:00
if (tr_count)
energy /= tr_count; // average non-inf energies
2020-05-06 17:09:15 +02:00
auto duty_sum_iter = pg_duty_sum.find(related_pg_pin);
2020-11-03 00:12:13 +01:00
float weight = 0.0;
if (duty_sum_iter != pg_duty_sum.end()) {
float duty_sum = duty_sum_iter->second;
2020-11-03 20:44:47 +01:00
if (duty_sum != 0.0)
weight = duty / duty_sum;
2020-11-03 00:12:13 +01:00
}
2020-05-01 18:40:26 +02:00
float port_internal = weight * energy * to_activity.activity();
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power", 2, "%3s -> %-3s %6s %.2f %.2f %.2f %9.2e %9.2e %s",
from_corner_port->name(),
to_port->name(),
when ? when->asString() : "",
to_activity.activity() * 1e-9,
duty,
weight,
energy,
port_internal,
related_pg_pin ? related_pg_pin : "no pg_pin");
2020-05-01 18:40:26 +02:00
internal += port_internal;
2018-11-26 18:15:52 +01:00
}
2020-05-02 16:48:48 +02:00
result.internal() += internal;
}
float
2020-05-02 06:58:25 +02:00
Power::findInputDuty(const Pin *to_pin,
const Instance *inst,
FuncExpr *func,
InternalPower *pwr)
2020-05-02 06:58:25 +02:00
{
2020-11-12 17:42:38 +01:00
const LibertyPort *from_corner_port = pwr->relatedPort();
if (from_corner_port) {
const LibertyPort *from_port = findLinkPort(network_->libertyCell(inst),
from_corner_port);
const Pin *from_pin = network_->findPin(inst, from_port);
2020-06-12 05:48:48 +02:00
if (from_pin) {
FuncExpr *when = pwr->when();
Vertex *from_vertex = graph_->pinLoadVertex(from_pin);
if (func && func->hasPort(from_port)) {
2020-11-02 21:39:36 +01:00
float from_activity = findActivity(from_pin).activity();
float to_activity = findActivity(to_pin).activity();
2020-06-12 05:48:48 +02:00
float duty1 = evalActivityDifference(func, inst, from_port).duty();
2020-11-03 02:22:49 +01:00
float duty = 0.0;
if (to_activity != 0.0F) {
duty = from_activity * duty1 / to_activity;
// Activities can get very small from multiplying probabilities
// through deep chains of logic. Dividing by very close to zero values
// can result in NaN/Inf depending on numerator.
if (!isnormal(duty))
duty = 0.0;
}
return duty;
2020-06-12 05:48:48 +02:00
}
else if (when)
return evalActivity(when, inst).duty();
else if (search_->isClock(from_vertex))
return 1.0;
return 0.5;
}
2020-05-02 06:58:25 +02:00
}
2020-06-12 05:48:48 +02:00
return 0.0;
2020-05-02 06:58:25 +02:00
}
2020-11-12 17:42:38 +01:00
// Hack to find cell port that corresponds to corner_port.
LibertyPort *
Power::findLinkPort(const LibertyCell *cell,
const LibertyPort *corner_port)
{
return cell->findLibertyPort(corner_port->name());
}
Pin *
Power::findLinkPin(const Instance *inst,
const LibertyPort *corner_port)
{
const LibertyCell *cell = network_->libertyCell(inst);
LibertyPort *port = findLinkPort(cell, corner_port);
return network_->findPin(inst, port);
}
2020-04-29 05:32:59 +02:00
static bool
isPositiveUnate(const LibertyCell *cell,
const LibertyPort *from,
const LibertyPort *to)
{
TimingArcSetSeq *arc_sets = cell->timingArcSets(from, to);
if (arc_sets && !arc_sets->empty()) {
TimingSense sense = (*arc_sets)[0]->sense();
return sense == TimingSense::positive_unate
|| sense == TimingSense::non_unate;
}
// default
return true;
}
2019-05-20 01:06:06 +02:00
////////////////////////////////////////////////////////////////
2018-11-26 18:15:52 +01:00
void
2019-04-01 18:05:07 +02:00
Power::findLeakagePower(const Instance *,
2018-11-26 18:15:52 +01:00
LibertyCell *cell,
2020-11-11 19:31:47 +01:00
const Corner *corner,
2018-11-26 18:15:52 +01:00
// Return values.
PowerResult &result)
{
2020-11-11 19:31:47 +01:00
int lib_ap_index = corner->libertyIndex(MinMax::max());
LibertyCell *corner_cell = cell->cornerCell(lib_ap_index);
2019-04-01 18:05:07 +02:00
float cond_leakage = 0.0;
bool found_cond = false;
float default_leakage = 0.0;
bool found_default = false;
2020-11-11 19:31:47 +01:00
for (LeakagePower *leak : *corner_cell->leakagePowers()) {
2018-11-26 18:15:52 +01:00
FuncExpr *when = leak->when();
if (when) {
2019-04-01 18:05:07 +02:00
FuncExprPortIterator port_iter(when);
2020-03-31 05:30:14 +02:00
float duty = 1.0;
2019-04-01 18:05:07 +02:00
while (port_iter.hasNext()) {
auto port = port_iter.next();
if (port->direction()->isAnyInput())
2020-03-31 05:30:14 +02:00
duty *= port->isClock() ? 0.25 : 0.5;
2018-11-26 18:15:52 +01:00
}
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power", 2, "leakage %s %s %.3e * %.2f",
cell->name(),
when->asString(),
leak->power(),
duty);
2019-04-01 18:05:07 +02:00
cond_leakage += leak->power() * duty;
found_cond = true;
2018-11-26 18:15:52 +01:00
}
2019-04-01 18:05:07 +02:00
else {
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power", 2, "leakage default %s %.3e",
cell->name(),
leak->power());
2019-04-01 18:05:07 +02:00
default_leakage += leak->power();
found_default = true;
}
}
float leakage = 0.0;
2020-03-31 05:30:14 +02:00
float cell_leakage;
bool cell_leakage_exists;
cell->leakagePower(cell_leakage, cell_leakage_exists);
if (cell_leakage_exists)
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power", 2, "leakage cell %s %.3e",
cell->name(),
cell_leakage);
2019-04-01 18:05:07 +02:00
// Ignore default leakages unless there are no conditional leakage groups.
2020-03-31 05:30:14 +02:00
if (found_cond)
2019-04-01 18:05:07 +02:00
leakage = cond_leakage;
else if (found_default)
leakage = default_leakage;
2020-03-31 05:30:14 +02:00
else if (cell_leakage_exists)
leakage = cell_leakage;
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power", 2, "leakage cell %s %.3e",
cell->name(),
leakage);
2020-05-02 16:48:48 +02:00
result.leakage() += leakage;
2018-11-26 18:15:52 +01:00
}
void
2018-12-05 23:18:41 +01:00
Power::findSwitchingPower(LibertyCell *cell,
2018-11-26 18:15:52 +01:00
const LibertyPort *to_port,
2019-04-29 17:39:05 +02:00
PwrActivity &activity,
2018-11-26 18:15:52 +01:00
float load_cap,
2020-11-11 19:31:47 +01:00
const Corner *corner,
2018-11-26 18:15:52 +01:00
// Return values.
PowerResult &result)
{
2020-11-11 19:31:47 +01:00
MinMax *mm = MinMax::max();
const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm);
int lib_ap_index = corner->libertyIndex(MinMax::max());
LibertyCell *corner_cell = cell->cornerCell(lib_ap_index);
float volt = portVoltage(corner_cell, to_port, dcalc_ap);
2019-04-29 17:39:05 +02:00
float switching = .5 * load_cap * volt * volt * activity.activity();
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e",
cell->name(),
to_port->name(),
activity.activity(),
volt,
switching);
2020-05-02 16:48:48 +02:00
result.switching() += switching;
2019-04-01 18:05:07 +02:00
}
2019-04-29 17:39:05 +02:00
PwrActivity
Power::findClkedActivity(const Pin *pin)
2019-04-29 17:39:05 +02:00
{
const Instance *inst = network_->instance(pin);
const Clock *inst_clk = findInstClk(inst);
return findClkedActivity(pin, inst_clk);
2019-04-29 17:39:05 +02:00
}
PwrActivity
Power::findClkedActivity(const Pin *pin,
const Clock *inst_clk)
2019-04-29 17:39:05 +02:00
{
2020-11-17 18:59:19 +01:00
PwrActivity activity = findActivity(pin);
2019-04-29 17:39:05 +02:00
const Clock *clk = findClk(pin);
if (clk == nullptr)
clk = inst_clk;
2018-11-26 18:15:52 +01:00
if (clk) {
2018-12-11 19:47:04 +01:00
float period = clk->period();
2020-11-17 18:59:19 +01:00
if (period > 0.0)
return PwrActivity(activity.activity() / period,
activity.duty(),
activity.origin());
2018-11-26 18:15:52 +01:00
}
2020-11-17 18:59:19 +01:00
return activity;
}
PwrActivity
Power::findActivity(const Pin *pin)
{
Vertex *vertex = graph_->pinLoadVertex(pin);
2020-11-13 00:57:33 +01:00
if (vertex && search_->isClock(vertex))
return PwrActivity(2.0, 0.5, PwrActivityOrigin::clock);
else if (global_activity_.isSet())
return global_activity_;
2020-11-17 18:59:19 +01:00
else if (vertex && vertex->isConstant())
return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant);
2020-07-30 16:55:48 +02:00
else if (activity_map_.hasKey(pin)) {
PwrActivity &activity = activity_map_[pin];
if (activity.origin() != PwrActivityOrigin::unknown)
return activity;
}
return input_activity_;
}
PwrActivity
Power::findSeqActivity(const Instance *inst,
LibertyPort *port)
{
if (global_activity_.isSet())
return global_activity_;
else if (hasSeqActivity(inst, port)) {
PwrActivity activity = seqActivity(inst, port);
if (activity.origin() != PwrActivityOrigin::unknown)
return activity;
}
return input_activity_;
}
float
Power::portVoltage(LibertyCell *cell,
const LibertyPort *port,
const DcalcAnalysisPt *dcalc_ap)
{
return pgNameVoltage(cell, port->relatedPowerPin(), dcalc_ap);
2018-11-26 18:15:52 +01:00
}
float
Power::pgNameVoltage(LibertyCell *cell,
const char *pg_port_name,
const DcalcAnalysisPt *dcalc_ap)
2018-11-26 18:15:52 +01:00
{
if (pg_port_name) {
auto pg_port = cell->findPgPort(pg_port_name);
if (pg_port) {
auto volt_name = pg_port->voltageName();
auto library = cell->libertyLibrary();
float voltage;
bool exists;
library->supplyVoltage(volt_name, voltage, exists);
if (exists)
return voltage;
}
}
2018-11-26 18:15:52 +01:00
const Pvt *pvt = dcalc_ap->operatingConditions();
2019-03-13 01:25:53 +01:00
if (pvt == nullptr)
2018-11-26 18:15:52 +01:00
pvt = cell->libertyLibrary()->defaultOperatingConditions();
if (pvt)
return pvt->voltage();
else
return 0.0;
}
2019-04-29 17:39:05 +02:00
const Clock *
Power::findClk(const Pin *to_pin)
2018-11-26 18:15:52 +01:00
{
2019-04-29 17:39:05 +02:00
const Clock *clk = nullptr;
2018-11-26 18:15:52 +01:00
Vertex *to_vertex = graph_->pinDrvrVertex(to_pin);
2020-11-12 22:04:22 +01:00
if (to_vertex) {
VertexPathIterator path_iter(to_vertex, this);
while (path_iter.hasNext()) {
PathVertex *path = path_iter.next();
const Clock *path_clk = path->clock(this);
if (path_clk
&& (clk == nullptr
|| path_clk->period() < clk->period()))
clk = path_clk;
}
2018-11-26 18:15:52 +01:00
}
2019-04-29 17:39:05 +02:00
return clk;
2018-11-26 18:15:52 +01:00
}
////////////////////////////////////////////////////////////////
PowerResult::PowerResult() :
internal_(0.0),
switching_(0.0),
leakage_(0.0)
{
}
void
PowerResult::clear()
{
internal_ = 0.0;
switching_ = 0.0;
leakage_ = 0.0;
}
float
PowerResult::total() const
{
return internal_ + switching_ + leakage_;
2018-11-26 18:15:52 +01:00
}
void
PowerResult::incr(PowerResult &result)
{
internal_ += result.internal_;
switching_ += result.switching_;
leakage_ += result.leakage_;
}
2019-04-29 17:39:05 +02:00
////////////////////////////////////////////////////////////////
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)
{
2020-11-03 02:22:49 +01:00
check();
2019-04-29 17:39:05 +02:00
}
void
PwrActivity::set(float activity,
float duty,
PwrActivityOrigin origin)
{
activity_ = activity;
duty_ = duty;
origin_ = origin;
2020-11-03 02:22:49 +01:00
check();
}
void
PwrActivity::check()
{
// Activities can get very small from multiplying probabilities
// through deep chains of logic. Clip them to prevent floating
// point anomalies.
if (abs(activity_) < min_activity)
activity_ = 0.0;
2019-04-29 17:39:05 +02:00
}
bool
PwrActivity::isSet() const
{
return origin_ != PwrActivityOrigin::unknown;
}
const char *
PwrActivity::originName() const
{
return pwr_activity_origin_map.find(origin_);
}
2018-11-26 18:15:52 +01:00
} // namespace