OpenSTA/search/Power.cc

444 lines
11 KiB
C++
Raw Normal View History

2018-11-26 18:15:52 +01: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
//
// 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/>.
#include <algorithm> // max
#include "Machine.hh"
#include "Debug.hh"
#include "Units.hh"
#include "Transition.hh"
#include "MinMax.hh"
#include "Liberty.hh"
#include "InternalPower.hh"
#include "LeakagePower.hh"
#include "TimingArc.hh"
#include "FuncExpr.hh"
#include "PortDirection.hh"
#include "Network.hh"
#include "Graph.hh"
#include "Sim.hh"
#include "Corner.hh"
#include "DcalcAnalysisPt.hh"
#include "GraphDelayCalc.hh"
#include "PathVertex.hh"
#include "Clock.hh"
#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-02-18 19:56:38 +01:00
typedef std::pair<float, int> SumCount;
typedef Map<const char*, SumCount, CharPtrLess> SupplySumCounts;
2018-11-26 18:15:52 +01:00
Power::Power(Sta *sta) :
StaState(sta),
sta_(sta),
default_signal_toggle_rate_(.1)
{
}
float
Power::defaultSignalToggleRate()
{
return default_signal_toggle_rate_;
}
void
Power::setDefaultSignalToggleRate(float rate)
{
default_signal_toggle_rate_ = rate;
}
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();
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);
if (cell)
power(inst, cell, corner, result);
}
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()
? graph_delay_calc_->loadCap(to_pin, TransRiseFall::rise(), dcalc_ap)
2018-11-26 18:15:52 +01:00
: 0.0;
float activity1;
bool is_clk;
activity(to_pin, inst_clk, activity1, is_clk);
2019-04-19 03:01:10 +02:00
if (to_port->direction()->isAnyOutput())
2018-12-05 23:18:41 +01:00
findSwitchingPower(cell, to_port, activity1, load_cap,
2018-11-26 18:15:52 +01:00
dcalc_ap, result);
2019-02-18 19:56:38 +01:00
findInternalPower(inst, cell, to_port, activity1,
2018-11-26 18:15:52 +01:00
load_cap, dcalc_ap, result);
}
delete pin_iter;
findLeakagePower(inst, cell, result);
}
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();
const Clock *clk = nullptr;
bool is_clk;
findClk(pin, clk, is_clk);
if (is_clk)
inst_clk = clk;
}
delete pin_iter;
return inst_clk;
}
2018-11-26 18:15:52 +01:00
void
Power::findInternalPower(const Instance *inst,
LibertyCell *cell,
const LibertyPort *to_port,
float activity,
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());
2019-02-18 19:56:38 +01:00
SupplySumCounts supply_sum_counts;
2018-11-26 18:15:52 +01:00
const Pvt *pvt = dcalc_ap->operatingConditions();
2019-02-18 19:56:38 +01:00
float duty = 1.0;
debugPrint2(debug_, "power", 2, " cap = %s duty = %.1f\n",
2018-11-26 18:15:52 +01:00
units_->capacitanceUnit()->asString(load_cap),
duty);
2018-12-05 23:18:41 +01:00
LibertyCellInternalPowerIterator pwr_iter(cell);
while (pwr_iter.hasNext()) {
InternalPower *pwr = pwr_iter.next();
2018-11-26 18:15:52 +01:00
const char *related_pg_pin = pwr->relatedPgPin();
2019-02-18 19:56:38 +01:00
const LibertyPort *from_port = pwr->relatedPort();
if (pwr->port() == to_port) {
2019-03-13 01:25:53 +01:00
if (from_port == nullptr)
2018-11-26 18:15:52 +01:00
from_port = to_port;
const Pin *from_pin = network_->findPin(inst, from_port);
Vertex *from_vertex = graph_->pinLoadVertex(from_pin);
2019-02-18 19:56:38 +01:00
float port_internal = 0.0;
2018-11-26 18:15:52 +01:00
TransRiseFallIterator tr_iter;
while (tr_iter.hasNext()) {
TransRiseFall *to_tr = tr_iter.next();
2019-02-18 19:56:38 +01:00
// Should use unateness to find from_tr.
2019-04-19 03:01:10 +02:00
TransRiseFall *from_tr = to_tr;
2018-12-11 19:47:04 +01:00
float slew = delayAsFloat(sta_->vertexSlew(from_vertex,
2019-04-19 03:01:10 +02:00
from_tr, dcalc_ap));
2019-02-18 19:56:38 +01:00
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;
2018-11-26 18:15:52 +01:00
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() : "",
2019-04-19 03:01:10 +02:00
related_pg_pin ? related_pg_pin : "(no pg_pin)");
2019-02-18 19:56:38 +01:00
debugPrint4(debug_, "power", 2, " slew = %s activity = %.2f/ns energy = %.5g pwr = %.5g\n",
2018-11-26 18:15:52 +01:00
units_->timeUnit()->asString(slew),
2019-02-18 19:56:38 +01:00
activity * 1e-9,
2018-11-26 18:15:52 +01:00
energy,
tr_internal);
}
2019-02-18 19:56:38 +01:00
// 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.
supply_sum_count.first += port_internal / 2.0;
supply_sum_count.second++;
2018-11-26 18:15:52 +01:00
}
}
2019-02-18 19:56:38 +01:00
float internal = 0.0;
for (auto supply_sum_count : supply_sum_counts) {
SumCount sum_count = supply_sum_count.second;
float supply_internal = sum_count.first;
int supply_count = sum_count.second;
2019-02-18 19:56:38 +01:00
internal += supply_internal / (supply_count > 0 ? supply_count : 1);
}
debugPrint1(debug_, "power", 2, " internal = %.5g\n", internal);
result.setInternal(result.internal() + internal);
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);
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;
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,
float activity,
float load_cap,
const DcalcAnalysisPt *dcalc_ap,
// Return values.
PowerResult &result)
{
float volt = voltage(cell, to_port, dcalc_ap);
2019-04-19 03:01:10 +02:00
float switching = .5 * load_cap * volt * volt * activity;
debugPrint5(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e\n",
cell->name(),
to_port->name(),
activity,
volt,
switching);
volt = voltage(cell, to_port, dcalc_ap);
result.setSwitching(result.switching() + switching);
2019-04-01 18:05:07 +02:00
}
2018-11-26 18:15:52 +01:00
void
Power::activity(const Pin *pin,
const Clock *inst_clk,
2018-11-26 18:15:52 +01:00
// Return values.
float &activity,
bool &is_clk)
{
const Clock *clk = inst_clk;
2018-11-26 18:15:52 +01:00
findClk(pin, clk, is_clk);
2018-12-11 19:47:04 +01:00
activity = 0.0;
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) {
if (is_clk)
activity = 2.0 / period;
else
activity = default_signal_toggle_rate_ / period;
2018-12-11 19:47:04 +01:00
}
2018-11-26 18:15:52 +01:00
}
}
float
Power::voltage(LibertyCell *cell,
const LibertyPort *port,
2018-11-26 18:15:52 +01:00
const DcalcAnalysisPt *dcalc_ap)
{
auto power_pin = port->relatedPowerPin();
if (power_pin) {
auto pg_port = cell->findPgPort(power_pin);
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;
}
void
Power::findClk(const Pin *to_pin,
// Return values.
const Clock *&clk,
bool &is_clk)
{
is_clk = false;
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;
if (path->isClock(this))
is_clk = true;
}
}
////////////////////////////////////////////////////////////////
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::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_;
}
} // namespace