2019-06-20 01:05:13 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2019-01-01 21:26:11 +01:00
|
|
|
// Copyright (c) 2019, 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
|
|
|
|
|
|
|
|
#include <algorithm> // max
|
|
|
|
|
#include "Machine.hh"
|
|
|
|
|
#include "Debug.hh"
|
2019-04-29 17:39:05 +02:00
|
|
|
#include "EnumNameMap.hh"
|
2018-11-26 18:15:52 +01:00
|
|
|
#include "Units.hh"
|
|
|
|
|
#include "Transition.hh"
|
|
|
|
|
#include "MinMax.hh"
|
2019-04-29 17:39:05 +02:00
|
|
|
#include "Clock.hh"
|
|
|
|
|
#include "TimingRole.hh"
|
2018-11-26 18:15:52 +01:00
|
|
|
#include "Liberty.hh"
|
|
|
|
|
#include "InternalPower.hh"
|
|
|
|
|
#include "LeakagePower.hh"
|
2019-04-29 17:39:05 +02:00
|
|
|
#include "Sequential.hh"
|
2018-11-26 18:15:52 +01:00
|
|
|
#include "TimingArc.hh"
|
|
|
|
|
#include "FuncExpr.hh"
|
|
|
|
|
#include "PortDirection.hh"
|
|
|
|
|
#include "Network.hh"
|
|
|
|
|
#include "Graph.hh"
|
|
|
|
|
#include "Corner.hh"
|
2019-04-29 17:39:05 +02:00
|
|
|
#include "Sdc.hh"
|
2018-11-26 18:15:52 +01:00
|
|
|
#include "DcalcAnalysisPt.hh"
|
|
|
|
|
#include "GraphDelayCalc.hh"
|
|
|
|
|
#include "PathVertex.hh"
|
2019-04-29 17:39:05 +02:00
|
|
|
#include "Levelize.hh"
|
|
|
|
|
#include "Sim.hh"
|
|
|
|
|
#include "Search.hh"
|
|
|
|
|
#include "Bfs.hh"
|
2018-11-26 18:15:52 +01:00
|
|
|
#include "Power.hh"
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
2019-05-08 22:23:59 +02:00
|
|
|
static int
|
|
|
|
|
funcExprPortCount(FuncExpr *expr);
|
2019-02-18 19:56:38 +01:00
|
|
|
|
2018-11-26 18:15:52 +01:00
|
|
|
Power::Power(Sta *sta) :
|
|
|
|
|
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) {
|
|
|
|
|
activity_map_[pin] = {activity, duty, PwrActivityOrigin::user};
|
|
|
|
|
activities_valid_ = false;
|
|
|
|
|
}
|
2018-11-26 18:15:52 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-29 17:39:05 +02:00
|
|
|
PwrActivity &
|
|
|
|
|
Power::pinActivity(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
return activity_map_[pin];
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 03:17:36 +02:00
|
|
|
bool
|
|
|
|
|
Power::hasPinActivity(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
return activity_map_.hasKey(pin);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-29 17:39:05 +02:00
|
|
|
void
|
|
|
|
|
Power::setPinActivity(const Pin *pin,
|
2019-05-08 22:23:59 +02:00
|
|
|
PwrActivity &activity)
|
2019-04-29 17:39:05 +02:00
|
|
|
{
|
2019-05-08 22:23:59 +02:00
|
|
|
activity_map_[pin] = activity;
|
2019-04-29 17:39:05 +02:00
|
|
|
activities_valid_ = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Power::setPinActivity(const Pin *pin,
|
2019-05-08 22:23:59 +02:00
|
|
|
float activity,
|
|
|
|
|
float duty,
|
|
|
|
|
PwrActivityOrigin origin)
|
2019-04-29 17:39:05 +02:00
|
|
|
{
|
2019-05-08 22:23:59 +02:00
|
|
|
activity_map_[pin] = {activity, duty, origin};
|
2019-04-29 17:39:05 +02:00
|
|
|
activities_valid_ = false;
|
|
|
|
|
}
|
2018-11-26 18:15:52 +01:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Power::power(const Corner *corner,
|
|
|
|
|
// Return values.
|
|
|
|
|
PowerResult &total,
|
|
|
|
|
PowerResult &sequential,
|
|
|
|
|
PowerResult &combinational,
|
|
|
|
|
PowerResult ¯o,
|
|
|
|
|
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;
|
|
|
|
|
power(inst, corner, inst_power);
|
|
|
|
|
if (cell->isMacro())
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
auto pin = vertex->pin();
|
|
|
|
|
debugPrint1(debug_, "power_activity", 3, "visit %s\n",
|
|
|
|
|
vertex->name(network_));
|
|
|
|
|
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_->pinActivity(from_pin);
|
|
|
|
|
PwrActivity to_activity(from_activity.activity(),
|
|
|
|
|
from_activity.duty(),
|
|
|
|
|
PwrActivityOrigin::propagated);
|
|
|
|
|
if (!power_->hasPinActivity(pin))
|
|
|
|
|
input_without_activity = true;
|
|
|
|
|
power_->setPinActivity(pin, to_activity);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Instance *inst = network_->instance(pin);
|
|
|
|
|
auto cell = network_->libertyCell(inst);
|
|
|
|
|
if (cell && cell->hasSequentials()) {
|
|
|
|
|
debugPrint1(debug_, "power_activity", 3, "pending reg %s\n",
|
|
|
|
|
network_->pathName(inst));
|
|
|
|
|
visited_regs_->insert(inst);
|
|
|
|
|
found_reg_without_activity_ = input_without_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);
|
2019-05-08 22:23:59 +02:00
|
|
|
debugPrint3(debug_, "power_activity", 3, "set %s %.2e %.2f\n",
|
|
|
|
|
vertex->name(network_),
|
|
|
|
|
activity.activity(),
|
|
|
|
|
activity.duty());
|
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)
|
|
|
|
|
{
|
|
|
|
|
switch (expr->op()) {
|
|
|
|
|
case FuncExpr::op_port: {
|
|
|
|
|
Pin *pin = network_->findPin(inst, expr->port()->name());
|
|
|
|
|
if (pin)
|
2019-05-08 22:23:59 +02:00
|
|
|
return findActivity(pin);
|
2019-06-19 16:55:04 +02:00
|
|
|
else
|
|
|
|
|
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);
|
|
|
|
|
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
|
|
|
|
|
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);
|
2019-05-01 03:17:36 +02:00
|
|
|
visitor.init();
|
2019-04-29 17:39:05 +02:00
|
|
|
bfs.visit(levelize_->maxLevel(), &visitor);
|
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;
|
2019-05-01 03:17:36 +02:00
|
|
|
visitor.init();
|
|
|
|
|
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)
|
|
|
|
|
{
|
2019-04-30 02:49:49 +02:00
|
|
|
for (auto vertex : levelize_->roots()) {
|
|
|
|
|
const Pin *pin = vertex->pin();
|
|
|
|
|
// Clock activities are baked in.
|
2019-05-01 03:17:36 +02:00
|
|
|
if (!sdc_->isVertexPinClock(pin)
|
|
|
|
|
&& network_->direction(pin) != PortDirection::internal()) {
|
2019-05-08 22:23:59 +02:00
|
|
|
debugPrint1(debug_, "power_activity", 3, "seed %s\n",
|
|
|
|
|
vertex->name(network_));
|
2019-04-30 02:49:49 +02:00
|
|
|
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);
|
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()) {
|
|
|
|
|
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()) {
|
|
|
|
|
debugPrint1(debug_, "power_activity", 3, "enqueue reg output %s\n",
|
|
|
|
|
vertex->name(network_));
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
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);
|
2019-04-20 23:07:00 +02:00
|
|
|
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;
|
2019-05-08 22:23:59 +02:00
|
|
|
PwrActivity activity = findClkedActivity(to_pin, inst_clk);
|
2019-04-19 03:01:10 +02:00
|
|
|
if (to_port->direction()->isAnyOutput())
|
2019-04-29 17:39:05 +02:00
|
|
|
findSwitchingPower(cell, to_port, activity, load_cap,
|
2018-11-26 18:15:52 +01:00
|
|
|
dcalc_ap, result);
|
2019-05-11 15:11:27 +02:00
|
|
|
findInternalPower(to_pin, to_port, inst, cell, activity,
|
|
|
|
|
load_cap, dcalc_ap, result);
|
2018-11-26 18:15:52 +01:00
|
|
|
}
|
|
|
|
|
delete pin_iter;
|
|
|
|
|
findLeakagePower(inst, cell, result);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-20 23:07:00 +02: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)
|
2019-04-20 23:07:00 +02:00
|
|
|
inst_clk = clk;
|
|
|
|
|
}
|
|
|
|
|
delete pin_iter;
|
|
|
|
|
return inst_clk;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-26 18:15:52 +01:00
|
|
|
void
|
2019-05-11 15:11:27 +02:00
|
|
|
Power::findInternalPower(const Pin *to_pin,
|
|
|
|
|
const LibertyPort *to_port,
|
2019-05-08 22:23:59 +02:00
|
|
|
const Instance *inst,
|
2018-11-26 18:15:52 +01:00
|
|
|
LibertyCell *cell,
|
2019-05-08 22:23:59 +02:00
|
|
|
PwrActivity &to_activity,
|
2018-11-26 18:15:52 +01:00
|
|
|
float load_cap,
|
|
|
|
|
const DcalcAnalysisPt *dcalc_ap,
|
|
|
|
|
// Return values.
|
|
|
|
|
PowerResult &result)
|
|
|
|
|
{
|
2019-02-18 19:56:38 +01:00
|
|
|
debugPrint3(debug_, "power", 2, "internal %s/%s (%s)\n",
|
2018-11-26 18:15:52 +01:00
|
|
|
network_->pathName(inst),
|
|
|
|
|
to_port->name(),
|
|
|
|
|
cell->name());
|
|
|
|
|
const Pvt *pvt = dcalc_ap->operatingConditions();
|
2019-04-29 17:39:05 +02:00
|
|
|
debugPrint1(debug_, "power", 2, " cap = %s\n",
|
|
|
|
|
units_->capacitanceUnit()->asString(load_cap));
|
|
|
|
|
debugPrint0(debug_, "power", 2, " when act/ns duty energy power\n");
|
2019-05-08 22:23:59 +02:00
|
|
|
float internal = 0.0;
|
2018-12-05 23:18:41 +01:00
|
|
|
LibertyCellInternalPowerIterator pwr_iter(cell);
|
|
|
|
|
while (pwr_iter.hasNext()) {
|
|
|
|
|
InternalPower *pwr = pwr_iter.next();
|
2019-02-18 19:56:38 +01:00
|
|
|
if (pwr->port() == to_port) {
|
2019-05-08 22:23:59 +02:00
|
|
|
const char *related_pg_pin = pwr->relatedPgPin();
|
|
|
|
|
const LibertyPort *from_port = pwr->relatedPort();
|
2019-04-29 17:39:05 +02:00
|
|
|
FuncExpr *when = pwr->when();
|
2019-05-20 01:06:06 +02:00
|
|
|
FuncExpr *infered_when = nullptr;
|
|
|
|
|
if (from_port) {
|
|
|
|
|
if (when == nullptr) {
|
|
|
|
|
FuncExpr *func = to_port->function();
|
|
|
|
|
if (func)
|
|
|
|
|
infered_when = inferedWhen(func, from_port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
from_port = to_port;
|
2019-05-08 22:23:59 +02:00
|
|
|
// If all the "when" clauses exist VSS internal power is ignored.
|
2019-06-20 01:05:13 +02:00
|
|
|
const Pin *from_pin = network_->findPin(inst, from_port);
|
|
|
|
|
if (from_pin
|
|
|
|
|
&& ((when && internalPowerMissingWhen(cell, to_port, related_pg_pin))
|
|
|
|
|
|| pgNameVoltage(cell, related_pg_pin, dcalc_ap) != 0.0)) {
|
|
|
|
|
Vertex *from_vertex = graph_->pinLoadVertex(from_pin);
|
|
|
|
|
float duty;
|
|
|
|
|
if (infered_when) {
|
|
|
|
|
PwrActivity from_activity = findActivity(from_pin);
|
|
|
|
|
PwrActivity to_activity = findActivity(to_pin);
|
|
|
|
|
float duty1 = evalActivity(infered_when, inst).duty();
|
|
|
|
|
if (to_activity.activity() == 0.0)
|
|
|
|
|
duty = 0.0;
|
2019-06-19 16:55:04 +02:00
|
|
|
else
|
2019-06-20 01:05:13 +02:00
|
|
|
duty = from_activity.activity() / to_activity.activity() * duty1;
|
|
|
|
|
}
|
|
|
|
|
else if (when)
|
|
|
|
|
duty = evalActivity(when, inst).duty();
|
|
|
|
|
else if (search_->isClock(from_vertex))
|
|
|
|
|
duty = 1.0;
|
|
|
|
|
else
|
|
|
|
|
duty = 0.5;
|
|
|
|
|
float port_energy = 0.0;
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto to_tr : TransRiseFall::range()) {
|
2019-06-20 01:05:13 +02:00
|
|
|
// Should use unateness to find from_tr.
|
|
|
|
|
TransRiseFall *from_tr = to_tr;
|
|
|
|
|
float slew = delayAsFloat(graph_->slew(from_vertex,
|
|
|
|
|
from_tr,
|
|
|
|
|
dcalc_ap->index()));
|
|
|
|
|
float table_energy = pwr->power(to_tr, pvt, slew, load_cap);
|
|
|
|
|
float tr_energy = table_energy * duty;
|
|
|
|
|
debugPrint4(debug_, "power", 3, " %s energy = %9.2e * %.2f = %9.2e\n",
|
|
|
|
|
to_tr->shortName(),
|
|
|
|
|
table_energy,
|
2019-05-08 22:23:59 +02:00
|
|
|
duty,
|
2019-06-20 01:05:13 +02:00
|
|
|
tr_energy);
|
|
|
|
|
port_energy += tr_energy;
|
2019-05-08 22:23:59 +02:00
|
|
|
}
|
2019-06-20 01:05:13 +02:00
|
|
|
float port_internal = port_energy * to_activity.activity();
|
|
|
|
|
debugPrint8(debug_, "power", 2, " %s -> %s %s %.2f %.2f %9.2e %9.2e %s\n",
|
|
|
|
|
from_port->name(),
|
|
|
|
|
to_port->name(),
|
|
|
|
|
when ? when->asString() : (infered_when ? infered_when->asString() : " "),
|
|
|
|
|
to_activity.activity() * 1e-9,
|
|
|
|
|
duty,
|
|
|
|
|
port_energy,
|
|
|
|
|
port_internal,
|
|
|
|
|
related_pg_pin ? related_pg_pin : "no pg_pin");
|
|
|
|
|
internal += port_internal;
|
2018-11-26 18:15:52 +01:00
|
|
|
}
|
2019-05-20 01:06:06 +02:00
|
|
|
if (infered_when)
|
|
|
|
|
infered_when->deleteSubexprs();
|
2018-11-26 18:15:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
2019-05-08 22:23:59 +02:00
|
|
|
result.setInternal(result.internal() + internal);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-20 01:06:06 +02:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
isPortRef(FuncExpr *expr,
|
|
|
|
|
const LibertyPort *port)
|
|
|
|
|
{
|
|
|
|
|
return (expr->op() == FuncExpr::op_port
|
|
|
|
|
&& expr->port() == port)
|
|
|
|
|
|| (expr->op() == FuncExpr::op_not
|
|
|
|
|
&& expr->left()->op() == FuncExpr::op_port
|
|
|
|
|
&& expr->left()->port() == port);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static FuncExpr *
|
|
|
|
|
negate(FuncExpr *expr)
|
|
|
|
|
{
|
|
|
|
|
if (expr->op() == FuncExpr::op_not)
|
|
|
|
|
return expr->left()->copy();
|
|
|
|
|
else
|
|
|
|
|
return FuncExpr::makeNot(expr->copy());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FuncExpr *
|
|
|
|
|
Power::inferedWhen(FuncExpr *expr,
|
|
|
|
|
const LibertyPort *from_port)
|
|
|
|
|
{
|
|
|
|
|
switch (expr->op()) {
|
|
|
|
|
case FuncExpr::op_port: {
|
|
|
|
|
if (expr->port() == from_port)
|
|
|
|
|
return FuncExpr::makeOne();
|
|
|
|
|
else
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
case FuncExpr::op_not:
|
|
|
|
|
return inferedWhen(expr->left(), from_port);
|
|
|
|
|
case FuncExpr::op_or:
|
|
|
|
|
case FuncExpr::op_xor: {
|
|
|
|
|
if (isPortRef(expr->left(), from_port))
|
|
|
|
|
return negate(expr->right());
|
|
|
|
|
if (isPortRef(expr->right(), from_port))
|
|
|
|
|
return negate(expr->left());
|
2019-08-13 06:36:32 +02:00
|
|
|
break;
|
2019-05-20 01:06:06 +02:00
|
|
|
}
|
|
|
|
|
case FuncExpr::op_and: {
|
|
|
|
|
if (isPortRef(expr->left(), from_port))
|
|
|
|
|
return expr->right()->copy();
|
|
|
|
|
if (isPortRef(expr->right(), from_port))
|
|
|
|
|
return expr->left()->copy();
|
2019-08-13 06:36:32 +02:00
|
|
|
break;
|
2019-05-20 01:06:06 +02:00
|
|
|
}
|
|
|
|
|
case FuncExpr::op_one:
|
|
|
|
|
case FuncExpr::op_zero:
|
2019-08-13 06:36:32 +02:00
|
|
|
break;
|
2019-05-20 01:06:06 +02:00
|
|
|
}
|
2019-07-02 16:07:34 +02:00
|
|
|
return nullptr;
|
2019-05-20 01:06:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2019-05-08 22:23:59 +02:00
|
|
|
// Return true if some a "when" clause for the internal power to_port
|
|
|
|
|
// is missing.
|
|
|
|
|
bool
|
|
|
|
|
Power::internalPowerMissingWhen(LibertyCell *cell,
|
|
|
|
|
const LibertyPort *to_port,
|
|
|
|
|
const char *related_pg_pin)
|
|
|
|
|
{
|
|
|
|
|
int when_input_count = 0;
|
|
|
|
|
int when_count = 0;
|
|
|
|
|
LibertyCellInternalPowerIterator pwr_iter(cell);
|
|
|
|
|
while (pwr_iter.hasNext()) {
|
|
|
|
|
auto pwr = pwr_iter.next();
|
|
|
|
|
auto when = pwr->when();
|
|
|
|
|
if (pwr->port() == to_port
|
|
|
|
|
&& pwr->relatedPort() == nullptr
|
2019-05-11 15:11:27 +02:00
|
|
|
&& stringEqIf(pwr->relatedPgPin(), related_pg_pin)
|
2019-05-08 22:23:59 +02:00
|
|
|
&& when) {
|
|
|
|
|
when_count++;
|
|
|
|
|
when_input_count = funcExprPortCount(when);
|
|
|
|
|
}
|
2019-02-18 19:56:38 +01:00
|
|
|
}
|
2019-05-08 22:23:59 +02:00
|
|
|
return when_count != (1 << when_input_count);
|
|
|
|
|
}
|
2019-02-18 19:56:38 +01:00
|
|
|
|
2019-05-08 22:23:59 +02:00
|
|
|
static int
|
|
|
|
|
funcExprPortCount(FuncExpr *expr)
|
|
|
|
|
{
|
|
|
|
|
int port_count = 0;
|
|
|
|
|
FuncExprPortIterator port_iter(expr);
|
|
|
|
|
while (port_iter.hasNext()) {
|
|
|
|
|
port_iter.next();
|
|
|
|
|
port_count++;
|
|
|
|
|
}
|
|
|
|
|
return port_count;
|
|
|
|
|
}
|
|
|
|
|
|
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,
|
|
|
|
|
// Return values.
|
|
|
|
|
PowerResult &result)
|
|
|
|
|
{
|
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;
|
2018-12-05 23:18:41 +01:00
|
|
|
LibertyCellLeakagePowerIterator pwr_iter(cell);
|
|
|
|
|
while (pwr_iter.hasNext()) {
|
|
|
|
|
LeakagePower *leak = pwr_iter.next();
|
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);
|
2019-04-20 23:07:00 +02:00
|
|
|
float duty = 2.0;
|
2019-04-01 18:05:07 +02:00
|
|
|
while (port_iter.hasNext()) {
|
|
|
|
|
auto port = port_iter.next();
|
|
|
|
|
if (port->direction()->isAnyInput())
|
|
|
|
|
duty *= .5;
|
2018-11-26 18:15:52 +01:00
|
|
|
}
|
2019-04-01 18:05:07 +02:00
|
|
|
debugPrint4(debug_, "power", 2, "leakage %s %s %.3e * %.2f\n",
|
|
|
|
|
cell->name(),
|
|
|
|
|
when->asString(),
|
|
|
|
|
leak->power(),
|
|
|
|
|
duty);
|
|
|
|
|
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 {
|
|
|
|
|
debugPrint2(debug_, "power", 2, "leakage default %s %.3e\n",
|
|
|
|
|
cell->name(),
|
|
|
|
|
leak->power());
|
|
|
|
|
default_leakage += leak->power();
|
|
|
|
|
found_default = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
float leakage = 0.0;
|
|
|
|
|
float leak;
|
|
|
|
|
bool exists;
|
|
|
|
|
cell->leakagePower(leak, exists);
|
|
|
|
|
if (exists) {
|
|
|
|
|
// Prefer cell_leakage_power until propagated activities exist.
|
2019-04-19 03:01:10 +02:00
|
|
|
debugPrint2(debug_, "power", 2, "leakage cell %s %.3e\n",
|
2019-04-01 18:05:07 +02:00
|
|
|
cell->name(),
|
|
|
|
|
leak);
|
|
|
|
|
leakage = leak;
|
2018-11-26 18:15:52 +01:00
|
|
|
}
|
2019-04-01 18:05:07 +02:00
|
|
|
// Ignore default leakages unless there are no conditional leakage groups.
|
|
|
|
|
else if (found_cond)
|
|
|
|
|
leakage = cond_leakage;
|
|
|
|
|
else if (found_default)
|
|
|
|
|
leakage = default_leakage;
|
2019-04-20 23:07:00 +02:00
|
|
|
result.setLeakage(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,
|
|
|
|
|
const DcalcAnalysisPt *dcalc_ap,
|
|
|
|
|
// Return values.
|
|
|
|
|
PowerResult &result)
|
|
|
|
|
{
|
2019-05-08 22:23:59 +02:00
|
|
|
float volt = portVoltage(cell, to_port, dcalc_ap);
|
2019-04-29 17:39:05 +02:00
|
|
|
float switching = .5 * load_cap * volt * volt * activity.activity();
|
2019-04-19 03:01:10 +02:00
|
|
|
debugPrint5(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e\n",
|
|
|
|
|
cell->name(),
|
|
|
|
|
to_port->name(),
|
2019-04-29 17:39:05 +02:00
|
|
|
activity.activity(),
|
2019-04-19 03:01:10 +02:00
|
|
|
volt,
|
|
|
|
|
switching);
|
2019-04-20 23:07:00 +02:00
|
|
|
result.setSwitching(result.switching() + switching);
|
2019-04-01 18:05:07 +02:00
|
|
|
}
|
|
|
|
|
|
2019-04-29 17:39:05 +02:00
|
|
|
PwrActivity
|
2019-05-08 22:23:59 +02:00
|
|
|
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);
|
2019-05-08 22:23:59 +02:00
|
|
|
return findClkedActivity(pin, inst_clk);
|
2019-04-29 17:39:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PwrActivity
|
2019-05-08 22:23:59 +02:00
|
|
|
Power::findClkedActivity(const Pin *pin,
|
|
|
|
|
const Clock *inst_clk)
|
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();
|
|
|
|
|
if (period > 0.0) {
|
2019-05-08 22:23:59 +02:00
|
|
|
PwrActivity activity = findActivity(pin);
|
|
|
|
|
return PwrActivity(activity.activity() / period,
|
|
|
|
|
activity.duty(),
|
|
|
|
|
activity.origin());
|
2018-12-11 19:47:04 +01:00
|
|
|
}
|
2018-11-26 18:15:52 +01:00
|
|
|
}
|
2019-05-08 22:23:59 +02:00
|
|
|
// gotta find a clock someplace...
|
|
|
|
|
return PwrActivity(input_activity_.activity(),
|
|
|
|
|
input_activity_.duty(),
|
|
|
|
|
PwrActivityOrigin::defaulted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PwrActivity
|
|
|
|
|
Power::findActivity(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
Vertex *vertex = graph_->pinLoadVertex(pin);
|
|
|
|
|
if (search_->isClock(vertex))
|
|
|
|
|
return PwrActivity(2.0, 0.5, PwrActivityOrigin::clock);
|
|
|
|
|
else if (global_activity_.isSet())
|
|
|
|
|
return global_activity_;
|
|
|
|
|
else {
|
|
|
|
|
if (activity_map_.hasKey(pin)) {
|
|
|
|
|
PwrActivity &activity = activity_map_[pin];
|
|
|
|
|
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
|
2019-05-08 22:23:59 +02:00
|
|
|
Power::pgNameVoltage(LibertyCell *cell,
|
|
|
|
|
const char *pg_port_name,
|
|
|
|
|
const DcalcAnalysisPt *dcalc_ap)
|
2018-11-26 18:15:52 +01:00
|
|
|
{
|
2019-05-08 22:23:59 +02:00
|
|
|
if (pg_port_name) {
|
|
|
|
|
auto pg_port = cell->findPgPort(pg_port_name);
|
2019-04-21 00:02:58 +02:00
|
|
|
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);
|
|
|
|
|
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
|
2019-03-13 01:25:53 +01:00
|
|
|
&& (clk == nullptr
|
2018-11-26 18:15:52 +01:00
|
|
|
|| path_clk->period() < clk->period()))
|
|
|
|
|
clk = path_clk;
|
|
|
|
|
}
|
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
|
|
|
|
|
{
|
2019-04-20 23:07:00 +02:00
|
|
|
return internal_ + switching_ + leakage_;
|
2018-11-26 18:15:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PowerResult::setInternal(float internal)
|
|
|
|
|
{
|
|
|
|
|
internal_ = internal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PowerResult::setLeakage(float leakage)
|
|
|
|
|
{
|
|
|
|
|
leakage_ = leakage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PowerResult::setSwitching(float switching)
|
|
|
|
|
{
|
|
|
|
|
switching_ = switching;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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_);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-26 18:15:52 +01:00
|
|
|
} // namespace
|