diff --git a/CMakeLists.txt b/CMakeLists.txt index acd81b72..28f362ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index 8ab9ea3f..707cc767 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -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. diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index 12083100..7bc4566a 100644 Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ diff --git a/doc/OpenSTA.pdf b/doc/OpenSTA.pdf index 8e7a3b33..69304a8c 100644 Binary files a/doc/OpenSTA.pdf and b/doc/OpenSTA.pdf differ diff --git a/graph/Delay.hh b/graph/Delay.hh index 6294695d..03b7e46a 100644 --- a/graph/Delay.hh +++ b/graph/Delay.hh @@ -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. diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc index 1c52a889..975b1812 100644 --- a/graph/DelayNormal1.cc +++ b/graph/DelayNormal1.cc @@ -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 diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc index b18c470f..d4b6978b 100644 --- a/graph/DelayNormal2.cc +++ b/graph/DelayNormal2.cc @@ -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 diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 24aba0e8..5a267ba8 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -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. diff --git a/search/Power.cc b/search/Power.cc index 90f8650e..b56b7fba 100644 --- a/search/Power.cc +++ b/search/Power.cc @@ -17,23 +17,30 @@ #include // 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 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 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 diff --git a/search/Power.hh b/search/Power.hh index 45a996ac..052e155c 100644 --- a/search/Power.hh +++ b/search/Power.hh @@ -22,6 +22,46 @@ namespace sta { class PowerResult; +class PwrActivity; +class PropActivityVisitor; +class BfsFwdIterator; + +typedef UnorderedMap 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 diff --git a/search/Property.cc b/search/Property.cc index 5d38021b..27e78b3e 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -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); diff --git a/search/Property.hh b/search/Property.hh index 2e906fbb..cdaeee3b 100644 --- a/search/Property.hh +++ b/search/Property.hh @@ -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_; }; }; diff --git a/tcl/Power.tcl b/tcl/Power.tcl index 28035766..765c852b 100644 --- a/tcl/Power.tcl +++ b/tcl/Power.tcl @@ -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 + } } } } diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index eec7996f..dabd06ad 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -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); } ////////////////////////////////////////////////////////////////