diff --git a/CMakeLists.txt b/CMakeLists.txt index a6b33856..80566fb4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,13 +66,13 @@ set(STA_SOURCE dcalc/ArnoldiReduce.cc dcalc/DcalcAnalysisPt.cc dcalc/DelayCalc.cc + dcalc/DelayCalcBase.cc dcalc/DmpCeff.cc dcalc/DmpDelayCalc.cc dcalc/GraphDelayCalc.cc - dcalc/GraphDelayCalc1.cc dcalc/LumpedCapDelayCalc.cc dcalc/NetCaps.cc - dcalc/RCDelayCalc.cc + dcalc/ParallelDelayCalc.cc dcalc/SlewDegradeDelayCalc.cc dcalc/UnitDelayCalc.cc @@ -97,6 +97,7 @@ set(STA_SOURCE liberty/Sequential.cc liberty/TableModel.cc liberty/TimingArc.cc + liberty/TimingModel.cc liberty/TimingRole.cc liberty/Units.cc liberty/Wireload.cc @@ -435,14 +436,14 @@ if (USE_CUDD) find_library(CUDD_LIB NAME cudd PATHS ${CUDD_DIR} - PATH_SUFFIXES lib lib/cudd + PATH_SUFFIXES lib lib/cudd cudd/.libs ) if (CUDD_LIB) message(STATUS "CUDD library: ${CUDD_LIB}") get_filename_component(CUDD_LIB_DIR "${CUDD_LIB}" PATH) get_filename_component(CUDD_LIB_PARENT1 "${CUDD_LIB_DIR}" PATH) find_file(CUDD_HEADER cudd.h - PATHS ${CUDD_LIB_PARENT1}/include ${CUDD_LIB_PARENT1}/include/cudd) + PATHS ${CUDD_LIB_PARENT1} ${CUDD_LIB_PARENT1}/include ${CUDD_LIB_PARENT1}/include/cudd) if (CUDD_HEADER) get_filename_component(CUDD_INCLUDE "${CUDD_HEADER}" PATH) message(STATUS "CUDD header: ${CUDD_HEADER}") diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index b3205010..15610b41 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -37,7 +37,7 @@ #include "DcalcAnalysisPt.hh" #include "DelayCalc.hh" #include "ArcDelayCalc.hh" -#include "RCDelayCalc.hh" +#include "LumpedCapDelayCalc.hh" #include "GraphDelayCalc.hh" #include "Arnoldi.hh" #include "ArnoldiReduce.hh" @@ -108,7 +108,7 @@ struct delay_work //////////////////////////////////////////////////////////////// -class ArnoldiDelayCalc : public RCDelayCalc +class ArnoldiDelayCalc : public LumpedCapDelayCalc { public: ArnoldiDelayCalc(StaState *sta); @@ -123,8 +123,7 @@ public: const RiseFall *rf, const Parasitic *parasitic, const DcalcAnalysisPt *dcalc_ap) override; - void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + void gateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -138,8 +137,7 @@ public: // Return values. ArcDelay &wire_delay, Slew &load_slew) override; - string reportGateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + string reportGateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -238,7 +236,7 @@ makeArnoldiDelayCalc(StaState *sta) } ArnoldiDelayCalc::ArnoldiDelayCalc(StaState *sta) : - RCDelayCalc(sta), + LumpedCapDelayCalc(sta), reduce_(new ArnoldiReduce(sta)), delay_work_(delay_work_create()) { @@ -321,7 +319,7 @@ ArnoldiDelayCalc::inputPortDelay(const Pin *drvr_pin, const Parasitic *parasitic, const DcalcAnalysisPt *dcalc_ap) { - RCDelayCalc::inputPortDelay(drvr_pin, in_slew, rf, parasitic, dcalc_ap); + LumpedCapDelayCalc::inputPortDelay(drvr_pin, in_slew, rf, parasitic, dcalc_ap); rcmodel_ = nullptr; _delayV[0] = 0.0; _slewV[0] = in_slew; @@ -358,8 +356,7 @@ ArnoldiDelayCalc::inputPortDelay(const Pin *drvr_pin, } void -ArnoldiDelayCalc::gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, +ArnoldiDelayCalc::gateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -372,6 +369,7 @@ ArnoldiDelayCalc::gateDelay(const LibertyCell *drvr_cell, { input_port_ = false; drvr_rf_ = arc->toEdge()->asRiseFall(); + const LibertyCell *drvr_cell = arc->from()->libertyCell(); drvr_library_ = drvr_cell->libertyLibrary(); drvr_parasitic_ = drvr_parasitic; ConcreteParasitic *drvr_cparasitic = @@ -384,9 +382,9 @@ ArnoldiDelayCalc::gateDelay(const LibertyCell *drvr_cell, related_out_cap, pvt, gate_delay, drvr_slew); else - LumpedCapDelayCalc::gateDelay(drvr_cell, arc, in_slew, load_cap, - drvr_parasitic, related_out_cap, pvt, - dcalc_ap, gate_delay, drvr_slew); + LumpedCapDelayCalc::gateDelay(arc, in_slew, load_cap, drvr_parasitic, + related_out_cap, pvt, dcalc_ap, + gate_delay, drvr_slew); drvr_slew_ = drvr_slew; multi_drvr_slew_factor_ = 1.0F; } @@ -455,8 +453,7 @@ ArnoldiDelayCalc::loadDelay(const Pin *load_pin, } string -ArnoldiDelayCalc::reportGateDelay(const LibertyCell *, - const TimingArc *, +ArnoldiDelayCalc::reportGateDelay(const TimingArc *, const Slew &, float, const Parasitic *, @@ -1313,9 +1310,7 @@ ArnoldiDelayCalc::ra_get_r(delay_work *D, c1 = ctot; ArcDelay d1; Slew s1; - tab->table->gateDelay(tab->cell, tab->pvt, tab->in_slew, - c1, tab->relcap, pocv_enabled_, - d1, s1); + tab->table->gateDelay(tab->pvt, tab->in_slew, c1, tab->relcap, pocv_enabled_, d1, s1); tlohi = slew_derate*delayAsFloat(s1); r = tlohi/(c_log*c1); if (rdelay>0.0 && r > rdelay) @@ -1337,8 +1332,7 @@ ArnoldiDelayCalc::ra_get_s(delay_work *D, double tlohi,smin,s; ArcDelay d1; Slew s1; - tab->table->gateDelay(tab->cell, tab->pvt, tab->in_slew, - c, tab->relcap, pocv_enabled_, d1, s1); + tab->table->gateDelay(tab->pvt, tab->in_slew, c, tab->relcap, pocv_enabled_, d1, s1); tlohi = slew_derate*delayAsFloat(s1); smin = r*c*c_smin; // c_smin = ra_hinv((1-vhi)/vhi-log(vhi)) + log(vhi); if (c_log*r*c >= tlohi) { @@ -1371,10 +1365,8 @@ ArnoldiDelayCalc::ra_rdelay_1(timing_table *tab, return 0.0; ArcDelay d1, d2; Slew s1, s2; - tab->table->gateDelay(tab->cell, tab->pvt, tab->in_slew, - c1, tab->relcap, pocv_enabled_, d1, s1); - tab->table->gateDelay(tab->cell, tab->pvt, tab->in_slew, - c2, tab->relcap, pocv_enabled_, d2, s2); + tab->table->gateDelay(tab->pvt, tab->in_slew, c1, tab->relcap, pocv_enabled_, d1, s1); + tab->table->gateDelay(tab->pvt, tab->in_slew, c2, tab->relcap, pocv_enabled_, d2, s2); double dt50 = delayAsFloat(d1)-delayAsFloat(d2); if (dt50 <= 0.0) return 0.0; @@ -1426,8 +1418,8 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, units_->timeUnit()->asString(s)); thix = ra_solve_for_t(p,s,vhi); tlox = ra_solve_for_t(p,s,vlo); - tab->table->gateDelay(tab->cell, tab->pvt,tab->in_slew, - ctot, tab->relcap, pocv_enabled_, df, sf); + tab->table->gateDelay(tab->pvt,tab->in_slew, ctot, tab->relcap, pocv_enabled_, + df, sf); debugPrint(debug_, "arnoldi", 1, "table slew (in_slew %s ctot %s) = %s", units_->timeUnit()->asString(tab->in_slew), units_->capacitanceUnit()->asString(ctot), @@ -1438,8 +1430,8 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, units_->timeUnit()->asString(tlox-thix)); } ceff = ctot; - tab->table->gateDelay(tab->cell, tab->pvt, tab->in_slew, - ceff, tab->relcap, pocv_enabled_, df, sf); + tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, tab->relcap, pocv_enabled_, + df, sf); t50_sy = delayAsFloat(df); t50_sr = ra_solve_for_t(1.0/(r*ceff),s,0.5); @@ -1480,8 +1472,7 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, units_->timeUnit()->asString(ceff_time), units_->capacitanceUnit()->asString(ceff)); - tab->table->gateDelay(tab->cell, tab->pvt, tab->in_slew, ceff, - tab->relcap, pocv_enabled_, df, sf); + tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, tab->relcap, pocv_enabled_, df, sf); t50_sy = delayAsFloat(df); t50_sr = ra_solve_for_t(1.0/(r*ceff),s,0.5); for (j=0;jn;j++) { diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc new file mode 100644 index 00000000..c2639664 --- /dev/null +++ b/dcalc/DelayCalcBase.cc @@ -0,0 +1,132 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2023, Parallax Software, Inc. +// +// 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 . + +#include "DelayCalcBase.hh" + +#include "Liberty.hh" +#include "TimingArc.hh" +#include "Network.hh" +#include "Parasitics.hh" + +namespace sta { + +DelayCalcBase::DelayCalcBase(StaState *sta) : + ArcDelayCalc(sta) +{ +} + +void +DelayCalcBase::finishDrvrPin() +{ + for (auto parasitic : unsaved_parasitics_) + parasitics_->deleteUnsavedParasitic(parasitic); + unsaved_parasitics_.clear(); + for (auto drvr_pin : reduced_parasitic_drvrs_) + parasitics_->deleteDrvrReducedParasitics(drvr_pin); + reduced_parasitic_drvrs_.clear(); +} + +void +DelayCalcBase::inputPortDelay(const Pin *, + float in_slew, + const RiseFall *rf, + const Parasitic *parasitic, + const DcalcAnalysisPt *) +{ + drvr_cell_ = nullptr; + drvr_library_ = network_->defaultLibertyLibrary(); + drvr_slew_ = in_slew; + drvr_rf_ = rf; + drvr_parasitic_ = parasitic; + input_port_ = true; +} + +void +DelayCalcBase::gateDelayInit(const TimingArc *arc, + const Slew &in_slew, + const Parasitic *drvr_parasitic) +{ + drvr_cell_ = arc->from()->libertyCell(); + drvr_library_ = drvr_cell_->libertyLibrary(); + drvr_rf_ = arc->toEdge()->asRiseFall(); + drvr_slew_ = in_slew; + drvr_parasitic_ = drvr_parasitic; + input_port_ = false; +} + +// For DSPF on an input port the elmore delay is used as the time +// constant of an exponential waveform. The delay to the logic +// threshold and slew are computed for the exponential waveform. +// Note that this uses the driver thresholds and relies on +// thresholdAdjust to convert the delay and slew to the load's thresholds. +void +DelayCalcBase::dspfWireDelaySlew(const Pin *, + float elmore, + ArcDelay &wire_delay, + Slew &load_slew) +{ + float vth = drvr_library_->inputThreshold(drvr_rf_); + float vl = drvr_library_->slewLowerThreshold(drvr_rf_); + float vh = drvr_library_->slewUpperThreshold(drvr_rf_); + float slew_derate = drvr_library_->slewDerateFromLibrary(); + wire_delay = -elmore * log(1.0 - vth); + load_slew = drvr_slew_ + elmore * log((1.0 - vl) / (1.0 - vh)) / slew_derate; +} + +void +DelayCalcBase::thresholdAdjust(const Pin *load_pin, + ArcDelay &load_delay, + Slew &load_slew) +{ + LibertyLibrary *load_library = thresholdLibrary(load_pin); + if (load_library + && drvr_library_ + && load_library != drvr_library_) { + float drvr_vth = drvr_library_->outputThreshold(drvr_rf_); + float load_vth = load_library->inputThreshold(drvr_rf_); + float drvr_slew_delta = drvr_library_->slewUpperThreshold(drvr_rf_) + - drvr_library_->slewLowerThreshold(drvr_rf_); + float load_delay_delta = + delayAsFloat(load_slew) * ((load_vth - drvr_vth) / drvr_slew_delta); + load_delay += (drvr_rf_ == RiseFall::rise()) + ? load_delay_delta + : -load_delay_delta; + float load_slew_delta = load_library->slewUpperThreshold(drvr_rf_) + - load_library->slewLowerThreshold(drvr_rf_); + float drvr_slew_derate = drvr_library_->slewDerateFromLibrary(); + float load_slew_derate = load_library->slewDerateFromLibrary(); + load_slew = load_slew * ((load_slew_delta / load_slew_derate) + / (drvr_slew_delta / drvr_slew_derate)); + } +} + +LibertyLibrary * +DelayCalcBase::thresholdLibrary(const Pin *load_pin) +{ + if (network_->isTopLevelPort(load_pin)) + // Input/output slews use the default (first read) library + // for slew thresholds. + return network_->defaultLibertyLibrary(); + else { + LibertyPort *lib_port = network_->libertyPort(load_pin); + if (lib_port) + return lib_port->libertyCell()->libertyLibrary(); + else + return network_->defaultLibertyLibrary(); + } +} + +} // namespace diff --git a/dcalc/RCDelayCalc.hh b/dcalc/DelayCalcBase.hh similarity index 58% rename from dcalc/RCDelayCalc.hh rename to dcalc/DelayCalcBase.hh index 24530959..248e8e5a 100644 --- a/dcalc/RCDelayCalc.hh +++ b/dcalc/DelayCalcBase.hh @@ -16,32 +16,48 @@ #pragma once -#include "LumpedCapDelayCalc.hh" +#include "ArcDelayCalc.hh" namespace sta { -// Base class for delay calculators with RC wire delay. -class RCDelayCalc : public LumpedCapDelayCalc +class DelayCalcBase : public ArcDelayCalc { public: - RCDelayCalc(StaState *sta); - ArcDelayCalc *copy() override; + explicit DelayCalcBase(StaState *sta); void inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, const Parasitic *parasitic, const DcalcAnalysisPt *dcalc_ap) override; + void finishDrvrPin() override; protected: + void gateDelayInit(const TimingArc *arc, + const Slew &in_slew, + const Parasitic *drvr_parasitic); + // Find the liberty library to use for logic/slew thresholds. + LibertyLibrary *thresholdLibrary(const Pin *load_pin); + // Adjust load_delay and load_slew from driver thresholds to load thresholds. + void thresholdAdjust(const Pin *load_pin, + ArcDelay &load_delay, + Slew &load_slew); // Helper function for input ports driving dspf parasitic. void dspfWireDelaySlew(const Pin *load_pin, float elmore, ArcDelay &wire_delay, Slew &load_slew); + Slew drvr_slew_; const LibertyCell *drvr_cell_; + const LibertyLibrary *drvr_library_; const Parasitic *drvr_parasitic_; bool input_port_; + const RiseFall *drvr_rf_; + // Parasitics returned by findParasitic that are reduced or estimated + // that can be deleted after delay calculation for the driver pin + // is finished. + Vector unsaved_parasitics_; + Vector reduced_parasitic_drvrs_; }; } // namespace diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 7ebdf5a6..0d298a78 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -381,7 +381,7 @@ DmpAlg::gateCapDelaySlew(double ceff, { ArcDelay model_delay; Slew model_slew; - gate_model_->gateDelay(drvr_cell_, pvt_, in_slew_, ceff, related_out_cap_, + gate_model_->gateDelay(pvt_, in_slew_, ceff, related_out_cap_, pocv_enabled_, model_delay, model_slew); delay = delayAsFloat(model_delay); slew = delayAsFloat(model_slew); @@ -1528,7 +1528,7 @@ testLuDecomp2() bool DmpCeffDelayCalc::unsuppored_model_warned_ = false; DmpCeffDelayCalc::DmpCeffDelayCalc(StaState *sta) : - RCDelayCalc(sta), + LumpedCapDelayCalc(sta), dmp_cap_(new DmpCap(sta)), dmp_pi_(new DmpPi(sta)), dmp_zero_c2_(new DmpZeroC2(sta)), @@ -1551,12 +1551,11 @@ DmpCeffDelayCalc::inputPortDelay(const Pin *port_pin, const DcalcAnalysisPt *dcalc_ap) { dmp_alg_ = nullptr; - RCDelayCalc::inputPortDelay(port_pin, in_slew, rf, parasitic, dcalc_ap); + LumpedCapDelayCalc::inputPortDelay(port_pin, in_slew, rf, parasitic, dcalc_ap); } void -DmpCeffDelayCalc::gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, +DmpCeffDelayCalc::gateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -1569,8 +1568,10 @@ DmpCeffDelayCalc::gateDelay(const LibertyCell *drvr_cell, { input_port_ = false; drvr_rf_ = arc->toEdge()->asRiseFall(); + const LibertyCell *drvr_cell = arc->from()->libertyCell(); drvr_library_ = drvr_cell->libertyLibrary(); drvr_parasitic_ = drvr_parasitic; + GateTimingModel *model = gateModel(arc, dcalc_ap); GateTableModel *table_model = dynamic_cast(model); if (table_model && drvr_parasitic) { @@ -1588,9 +1589,9 @@ DmpCeffDelayCalc::gateDelay(const LibertyCell *drvr_cell, drvr_slew = dmp_drvr_slew; } else { - LumpedCapDelayCalc::gateDelay(drvr_cell, arc, in_slew, load_cap, - drvr_parasitic, related_out_cap, pvt, - dcalc_ap, gate_delay, drvr_slew); + LumpedCapDelayCalc::gateDelay(arc, in_slew, load_cap, drvr_parasitic, + related_out_cap, pvt, dcalc_ap, + gate_delay, drvr_slew); if (drvr_parasitic && !unsuppored_model_warned_) { unsuppored_model_warned_ = true; @@ -1646,8 +1647,7 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, } float -DmpCeffDelayCalc::ceff(const LibertyCell *drvr_cell, - const TimingArc *arc, +DmpCeffDelayCalc::ceff(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -1657,8 +1657,7 @@ DmpCeffDelayCalc::ceff(const LibertyCell *drvr_cell, { ArcDelay gate_delay; Slew drvr_slew; - gateDelay(drvr_cell, arc, in_slew, load_cap, - drvr_parasitic, related_out_cap, pvt, dcalc_ap, + gateDelay(arc, in_slew, load_cap, drvr_parasitic, related_out_cap, pvt, dcalc_ap, gate_delay, drvr_slew); if (dmp_alg_) return dmp_alg_->ceff(); @@ -1667,8 +1666,7 @@ DmpCeffDelayCalc::ceff(const LibertyCell *drvr_cell, } string -DmpCeffDelayCalc::reportGateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, +DmpCeffDelayCalc::reportGateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -1679,14 +1677,14 @@ DmpCeffDelayCalc::reportGateDelay(const LibertyCell *drvr_cell, { ArcDelay gate_delay; Slew drvr_slew; - gateDelay(drvr_cell, arc, in_slew, load_cap, - drvr_parasitic, related_out_cap, pvt, dcalc_ap, + gateDelay(arc, in_slew, load_cap, drvr_parasitic, related_out_cap, pvt, dcalc_ap, gate_delay, drvr_slew); GateTimingModel *model = gateModel(arc, dcalc_ap); float c_eff = 0.0; string result; if (drvr_parasitic_ && dmp_alg_) { c_eff = dmp_alg_->ceff(); + const LibertyCell *drvr_cell = arc->from()->libertyCell(); const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); const Units *units = drvr_library->units(); const Unit *cap_unit = units->capacitanceUnit(); @@ -1707,8 +1705,8 @@ DmpCeffDelayCalc::reportGateDelay(const LibertyCell *drvr_cell, c_eff = load_cap; if (model) { float in_slew1 = delayAsFloat(in_slew); - result += model->reportGateDelay(drvr_cell, pvt, in_slew1, c_eff, - related_out_cap, pocv_enabled_, digits); + result += model->reportGateDelay(pvt, in_slew1, c_eff, related_out_cap, + pocv_enabled_, digits); } return result; } @@ -1728,10 +1726,8 @@ gateModelRd(const LibertyCell *cell, float cap2 = cap1 + 1e-15; ArcDelay d1, d2; Slew s1, s2; - gate_model->gateDelay(cell, pvt, in_slew, cap1, related_out_cap, pocv_enabled, - d1, s1); - gate_model->gateDelay(cell, pvt, in_slew, cap2, related_out_cap, pocv_enabled, - d2, s2); + gate_model->gateDelay(pvt, in_slew, cap1, related_out_cap, pocv_enabled, d1, s1); + gate_model->gateDelay(pvt, in_slew, cap2, related_out_cap, pocv_enabled, d2, s2); double vth = cell->libertyLibrary()->outputThreshold(rf); float rd = -log(vth) * abs(delayAsFloat(d1) - delayAsFloat(d2)) / (cap2 - cap1); return rd; diff --git a/dcalc/DmpCeff.hh b/dcalc/DmpCeff.hh index 5f8360d2..b79dbdc6 100644 --- a/dcalc/DmpCeff.hh +++ b/dcalc/DmpCeff.hh @@ -17,7 +17,7 @@ #pragma once #include "LibertyClass.hh" -#include "RCDelayCalc.hh" +#include "LumpedCapDelayCalc.hh" namespace sta { @@ -29,7 +29,7 @@ class GateTableModel; // Delay calculator using Dartu/Menezes/Pileggi effective capacitance // algorithm for RSPF loads. -class DmpCeffDelayCalc : public RCDelayCalc +class DmpCeffDelayCalc : public LumpedCapDelayCalc { public: DmpCeffDelayCalc(StaState *sta); @@ -39,8 +39,7 @@ public: const RiseFall *rf, const Parasitic *parasitic, const DcalcAnalysisPt *dcalc_ap); - virtual void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + virtual void gateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -50,16 +49,14 @@ public: // return values ArcDelay &gate_delay, Slew &drvr_slew); - virtual float ceff(const LibertyCell *drvr_cell, - const TimingArc *arc, + virtual float ceff(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, float related_out_cap, const Pvt *pvt, const DcalcAnalysisPt *dcalc_ap); - virtual string reportGateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + virtual string reportGateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index 169b1051..ed97468a 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -35,17 +35,6 @@ class DmpCeffElmoreDelayCalc : public DmpCeffDelayCalc public: DmpCeffElmoreDelayCalc(StaState *sta); ArcDelayCalc *copy() override; - void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *drvr_parasitic, - float related_out_cap, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - ArcDelay &gate_, - Slew &drvr_slew) override; void loadDelay(const Pin *load_pin, ArcDelay &wire_delay, Slew &load_slew) override; @@ -68,25 +57,6 @@ DmpCeffElmoreDelayCalc::copy() return new DmpCeffElmoreDelayCalc(this); } -void -DmpCeffElmoreDelayCalc::gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *drvr_parasitic, - float related_out_cap, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) -{ - DmpCeffDelayCalc::gateDelay(drvr_cell, arc, in_slew, - load_cap, drvr_parasitic, related_out_cap, - pvt, dcalc_ap, - gate_delay, drvr_slew); -} - void DmpCeffElmoreDelayCalc::loadDelay(const Pin *load_pin, ArcDelay &wire_delay, @@ -127,8 +97,7 @@ public: const RiseFall *rf, const Parasitic *parasitic, const DcalcAnalysisPt *dcalc_ap) override; - void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + void gateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -233,7 +202,7 @@ DmpCeffTwoPoleDelayCalc::findParasitic(const Pin *drvr_pin, cnst_min_max, parasitic_ap); // Estimated parasitics are not recorded in the "database", so - // it for deletion after the drvr pin delay calc is finished. + // save it for deletion after the drvr pin delay calc is finished. if (parasitic) unsaved_parasitics_.push_back(parasitic); } @@ -260,8 +229,7 @@ DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *port_pin, } void -DmpCeffTwoPoleDelayCalc::gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, +DmpCeffTwoPoleDelayCalc::gateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -272,15 +240,13 @@ DmpCeffTwoPoleDelayCalc::gateDelay(const LibertyCell *drvr_cell, ArcDelay &gate_delay, Slew &drvr_slew) { + gateDelayInit(arc, in_slew, drvr_parasitic); parasitic_is_pole_residue_ = parasitics_->isPiPoleResidue(drvr_parasitic); - const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); - const RiseFall *rf = arc->toEdge()->asRiseFall(); - vth_ = drvr_library->outputThreshold(rf); - vl_ = drvr_library->slewLowerThreshold(rf); - vh_ = drvr_library->slewUpperThreshold(rf); - slew_derate_ = drvr_library->slewDerateFromLibrary(); - DmpCeffDelayCalc::gateDelay(drvr_cell, arc, in_slew, - load_cap, drvr_parasitic, + vth_ = drvr_library_->outputThreshold(drvr_rf_); + vl_ = drvr_library_->slewLowerThreshold(drvr_rf_); + vh_ = drvr_library_->slewUpperThreshold(drvr_rf_); + slew_derate_ = drvr_library_->slewDerateFromLibrary(); + DmpCeffDelayCalc::gateDelay(arc, in_slew, load_cap, drvr_parasitic, related_out_cap, pvt, dcalc_ap, gate_delay, drvr_slew); } diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index d559e29d..ef8735db 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -16,100 +16,1356 @@ #include "GraphDelayCalc.hh" +#include "Debug.hh" +#include "Stats.hh" +#include "MinMax.hh" +#include "Mutex.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" #include "Liberty.hh" +#include "PortDirection.hh" #include "Network.hh" -#include "Graph.hh" +#include "InputDrive.hh" #include "Sdc.hh" +#include "Graph.hh" +#include "Parasitics.hh" +#include "search/Levelize.hh" #include "Corner.hh" +#include "SearchPred.hh" +#include "Bfs.hh" +#include "ArcDelayCalc.hh" +#include "DcalcAnalysisPt.hh" +#include "NetCaps.hh" +#include "ClkNetwork.hh" namespace sta { +using std::abs; + +static const Slew default_slew = 0.0; + +static bool +isLeafDriver(const Pin *pin, + const Network *network); + GraphDelayCalc::GraphDelayCalc(StaState *sta) : - StaState(sta) + StaState(sta), + observer_(nullptr), + delays_seeded_(false), + incremental_(false), + delays_exist_(false), + invalid_delays_(new VertexSet(graph_)), + search_pred_(new SearchPred1(sta)), + search_non_latch_pred_(new SearchPredNonLatch2(sta)), + clk_pred_(new ClkTreeSearchPred(sta)), + iter_(new BfsFwdIterator(BfsIndex::dcalc, search_non_latch_pred_, sta)), + multi_drvr_nets_found_(false), + incremental_delay_tolerance_(0.0) { } +GraphDelayCalc::~GraphDelayCalc() +{ + delete search_pred_; + delete invalid_delays_; + delete search_non_latch_pred_; + delete clk_pred_; + delete iter_; + deleteMultiDrvrNets(); + delete observer_; +} + void -GraphDelayCalc::setObserver(DelayCalcObserver *observer) +GraphDelayCalc::deleteMultiDrvrNets() { - // Observer not needed by GraphDelayCalc. - delete observer; + Set drvr_nets; + MultiDrvrNetMap::Iterator multi_iter(multi_drvr_net_map_); + while (multi_iter.hasNext()) { + MultiDrvrNet *multi_drvr = multi_iter.next(); + // Multiple drvr pins point to the same drvr PinSet, + // so collect them into a set. + drvr_nets.insert(multi_drvr); + } + multi_drvr_net_map_.clear(); + drvr_nets.deleteContents(); } -string -GraphDelayCalc::reportDelayCalc(Edge *, - TimingArc *, - const Corner *, - const MinMax *, - int) +void +GraphDelayCalc::copyState(const StaState *sta) { - return ""; + StaState::copyState(sta); + // Notify sub-components. + iter_->copyState(sta); +} + +void +GraphDelayCalc::clear() +{ + delaysInvalid(); + deleteMultiDrvrNets(); + multi_drvr_nets_found_ = false; } float GraphDelayCalc::incrementalDelayTolerance() { - return 0.0; + return incremental_delay_tolerance_; } void -GraphDelayCalc::loadCap(const Pin *, - const Parasitic *, - const RiseFall *, - const DcalcAnalysisPt *, - // Return values. - float &pin_cap, - float &wire_cap) const +GraphDelayCalc::setIncrementalDelayTolerance(float tol) { - pin_cap = wire_cap = 0.0F; -} - -float -GraphDelayCalc::loadCap(const Pin *, - const RiseFall *, - const DcalcAnalysisPt *) const -{ - return 0.0F; -} - -float -GraphDelayCalc::loadCap(const Pin *, - const Parasitic *, - const RiseFall *, - const DcalcAnalysisPt *) const -{ - return 0.0F; -} - -float -GraphDelayCalc::loadCap(const Pin *, - const DcalcAnalysisPt *) const -{ - return 0.0F; + incremental_delay_tolerance_ = tol; } void -GraphDelayCalc::netCaps(const Pin *, - const RiseFall *, - const DcalcAnalysisPt *, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_set_load) const +GraphDelayCalc::setObserver(DelayCalcObserver *observer) { - pin_cap = wire_cap = fanout = 0.0F; - has_set_load = false; + delete observer_; + observer_ = observer; +} + +void +GraphDelayCalc::delaysInvalid() +{ + debugPrint(debug_, "delay_calc", 1, "delays invalid"); + delays_exist_ = false; + delays_seeded_ = false; + incremental_ = false; + iter_->clear(); + // No need to keep track of incremental updates any more. + invalid_delays_->clear(); + invalid_check_edges_.clear(); + invalid_latch_edges_.clear(); +} + +void +GraphDelayCalc::delayInvalid(const Pin *pin) +{ + if (graph_ && incremental_) { + if (network_->isHierarchical(pin)) { + EdgesThruHierPinIterator edge_iter(pin, network_, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + delayInvalid(edge->from(graph_)); + } + } + else { + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + delayInvalid(vertex); + if (bidirect_drvr_vertex) + delayInvalid(bidirect_drvr_vertex); + } + } +} + +void +GraphDelayCalc::delayInvalid(Vertex *vertex) +{ + debugPrint(debug_, "delay_calc", 2, "delay invalid %s", + vertex->name(sdc_network_)); + if (graph_ && incremental_) { + invalid_delays_->insert(vertex); + // Invalidate driver that triggers dcalc for multi-driver nets. + MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); + if (multi_drvr) + invalid_delays_->insert(multi_drvr->dcalcDrvr()); + } +} + +void +GraphDelayCalc::deleteVertexBefore(Vertex *vertex) +{ + iter_->deleteVertexBefore(vertex); + if (incremental_) + invalid_delays_->erase(vertex); + MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); + if (multi_drvr) { + multi_drvr->drvrs()->erase(vertex); + multi_drvr_net_map_.erase(vertex); + } +} + +//////////////////////////////////////////////////////////////// + +class FindVertexDelays : public VertexVisitor +{ +public: + FindVertexDelays(GraphDelayCalc *graph_delay_calc1); + virtual ~FindVertexDelays(); + virtual void visit(Vertex *vertex); + virtual VertexVisitor *copy() const; + +protected: + GraphDelayCalc *graph_delay_calc1_; + ArcDelayCalc *arc_delay_calc_; +}; + +FindVertexDelays::FindVertexDelays(GraphDelayCalc *graph_delay_calc1) : + VertexVisitor(), + graph_delay_calc1_(graph_delay_calc1), + arc_delay_calc_(graph_delay_calc1_->arc_delay_calc_->copy()) +{ +} + +FindVertexDelays::~FindVertexDelays() +{ + delete arc_delay_calc_; +} + +VertexVisitor * +FindVertexDelays::copy() const +{ + // Copy StaState::arc_delay_calc_ because it needs separate state + // for each thread. + return new FindVertexDelays(graph_delay_calc1_); +} + +void +FindVertexDelays::visit(Vertex *vertex) +{ + graph_delay_calc1_->findVertexDelay(vertex, arc_delay_calc_, true); +} + +// The logical structure of incremental delay calculation closely +// resembles the incremental search arrival time algorithm +// (Search::findArrivals). +void +GraphDelayCalc::findDelays(Level level) +{ + if (arc_delay_calc_) { + Stats stats(debug_, report_); + int dcalc_count = 0; + debugPrint(debug_, "delay_calc", 1, "find delays to level %d", level); + if (!delays_seeded_) { + iter_->clear(); + seedRootSlews(); + delays_seeded_ = true; + } + else + iter_->ensureSize(); + if (incremental_) + seedInvalidDelays(); + + FindVertexDelays visitor(this); + dcalc_count += iter_->visitParallel(level, &visitor); + + // Timing checks require slews at both ends of the arc, + // so find their delays after all slews are known. + for (Edge *check_edge : invalid_check_edges_) + findCheckEdgeDelays(check_edge, arc_delay_calc_); + invalid_check_edges_.clear(); + + for (Edge *latch_edge : invalid_latch_edges_) + findLatchEdgeDelays(latch_edge); + invalid_latch_edges_.clear(); + + delays_exist_ = true; + incremental_ = true; + debugPrint(debug_, "delay_calc", 1, "found %d delays", dcalc_count); + stats.report("Delay calc"); + } +} + +void +GraphDelayCalc::seedInvalidDelays() +{ + for (Vertex *vertex : *invalid_delays_) { + if (vertex->isRoot()) + seedRootSlew(vertex, arc_delay_calc_); + else { + if (search_non_latch_pred_->searchFrom(vertex)) + iter_->enqueue(vertex); + } + } + invalid_delays_->clear(); +} + +void +GraphDelayCalc::seedRootSlews() +{ + for (Vertex *vertex : *levelize_->roots()) + seedRootSlew(vertex, arc_delay_calc_); +} + +void +GraphDelayCalc::seedRootSlew(Vertex *vertex, + ArcDelayCalc *arc_delay_calc) +{ + if (vertex->isDriver(network_)) + seedDrvrSlew(vertex, arc_delay_calc); + else + seedLoadSlew(vertex); + iter_->enqueueAdjacentVertices(vertex); +} + +void +GraphDelayCalc::seedDrvrSlew(Vertex *drvr_vertex, + ArcDelayCalc *arc_delay_calc) +{ + const Pin *drvr_pin = drvr_vertex->pin(); + debugPrint(debug_, "delay_calc", 2, "seed driver slew %s", + drvr_vertex->name(sdc_network_)); + InputDrive *drive = 0; + if (network_->isTopLevelPort(drvr_pin)) { + Port *port = network_->port(drvr_pin); + drive = sdc_->findInputDrive(port); + } + for (auto rf : RiseFall::range()) { + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + if (drive) { + const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); + const LibertyCell *drvr_cell; + const LibertyPort *from_port, *to_port; + float *from_slews; + drive->driveCell(rf, cnst_min_max, drvr_cell, from_port, + from_slews, to_port); + if (drvr_cell) { + if (from_port == nullptr) + from_port = driveCellDefaultFromPort(drvr_cell, to_port); + findInputDriverDelay(drvr_cell, drvr_pin, drvr_vertex, rf, + from_port, from_slews, to_port, dcalc_ap); + } + else + seedNoDrvrCellSlew(drvr_vertex, drvr_pin, rf, drive, dcalc_ap, + arc_delay_calc); + } + else + seedNoDrvrSlew(drvr_vertex, drvr_pin, rf, dcalc_ap, arc_delay_calc); + } + } +} + +void +GraphDelayCalc::seedNoDrvrCellSlew(Vertex *drvr_vertex, + const Pin *drvr_pin, + const RiseFall *rf, + InputDrive *drive, + DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc) +{ + DcalcAPIndex ap_index = dcalc_ap->index(); + const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); + Slew slew = default_slew; + float drive_slew; + bool exists; + drive->slew(rf, cnst_min_max, drive_slew, exists); + if (exists) + slew = drive_slew; + else { + // Top level bidirect driver uses load slew unless + // bidirect instance paths are disabled. + if (sdc_->bidirectDrvrSlewFromLoad(drvr_pin)) { + Vertex *load_vertex = graph_->pinLoadVertex(drvr_pin); + slew = graph_->slew(load_vertex, rf, ap_index); + } + } + Delay drive_delay = delay_zero; + float drive_res; + drive->driveResistance(rf, cnst_min_max, drive_res, exists); + Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, dcalc_ap); + if (exists) { + float cap = loadCap(drvr_pin, parasitic, rf, dcalc_ap); + drive_delay = cap * drive_res; + slew = cap * drive_res; + } + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) + graph_->setSlew(drvr_vertex, rf, ap_index, slew); + arc_delay_calc->inputPortDelay(drvr_pin, delayAsFloat(slew), rf, + parasitic, dcalc_ap); + annotateLoadDelays(drvr_vertex, rf, drive_delay, false, dcalc_ap, + arc_delay_calc); + arc_delay_calc->finishDrvrPin(); +} + +void +GraphDelayCalc::seedNoDrvrSlew(Vertex *drvr_vertex, + const Pin *drvr_pin, + const RiseFall *rf, + DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc) +{ + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + DcalcAPIndex ap_index = dcalc_ap->index(); + Slew slew(default_slew); + // Top level bidirect driver uses load slew unless + // bidirect instance paths are disabled. + if (sdc_->bidirectDrvrSlewFromLoad(drvr_pin)) { + Vertex *load_vertex = graph_->pinLoadVertex(drvr_pin); + slew = graph_->slew(load_vertex, rf, ap_index); + } + if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) + graph_->setSlew(drvr_vertex, rf, ap_index, slew); + Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, dcalc_ap); + arc_delay_calc->inputPortDelay(drvr_pin, delayAsFloat(slew), rf, + parasitic, dcalc_ap); + annotateLoadDelays(drvr_vertex, rf, delay_zero, false, dcalc_ap, + arc_delay_calc); + arc_delay_calc->finishDrvrPin(); +} + +void +GraphDelayCalc::seedLoadSlew(Vertex *vertex) +{ + const Pin *pin = vertex->pin(); + debugPrint(debug_, "delay_calc", 2, "seed load slew %s", + vertex->name(sdc_network_)); + ClockSet *clks = sdc_->findLeafPinClocks(pin); + initSlew(vertex); + for (auto rf : RiseFall::range()) { + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + if (!vertex->slewAnnotated(rf, slew_min_max)) { + float slew = 0.0; + if (clks) { + slew = slew_min_max->initValue(); + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + float clk_slew = clk->slew(rf, slew_min_max); + if (slew_min_max->compare(clk_slew, slew)) + slew = clk_slew; + } + } + DcalcAPIndex ap_index = dcalc_ap->index(); + graph_->setSlew(vertex, rf, ap_index, slew); + } + } + } +} + +// If a driving cell does not specify a -from_pin, the first port +// defined in the cell that has a timing group to the output port +// is used. Not exactly reasonable, but it's compatible. +LibertyPort * +GraphDelayCalc::driveCellDefaultFromPort(const LibertyCell *cell, + const LibertyPort *to_port) +{ + LibertyPort *from_port = 0; + int from_port_index = 0; + for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, to_port)) { + LibertyPort *set_from_port = arc_set->from(); + int set_from_port_index = findPortIndex(cell, set_from_port); + if (from_port == nullptr + || set_from_port_index < from_port_index) { + from_port = set_from_port; + from_port_index = set_from_port_index; + } + } + return from_port; +} + +// Find the index that port is defined in cell. +int +GraphDelayCalc::findPortIndex(const LibertyCell *cell, + const LibertyPort *port) +{ + int index = 0; + LibertyCellPortIterator port_iter(cell); + while (port_iter.hasNext()) { + LibertyPort *cell_port = port_iter.next(); + if (cell_port == port) + return index; + index++; + } + report_->critical(207, "port not found in cell"); + return 0; +} + +void +GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, + const Pin *drvr_pin, + Vertex *drvr_vertex, + const RiseFall *rf, + const LibertyPort *from_port, + float *from_slews, + const LibertyPort *to_port, + const DcalcAnalysisPt *dcalc_ap) +{ + debugPrint(debug_, "delay_calc", 2, " driver cell %s %s", + drvr_cell->name(), + rf->asString()); + for (TimingArcSet *arc_set : drvr_cell->timingArcSets(from_port, to_port)) { + for (TimingArc *arc : arc_set->arcs()) { + if (arc->toEdge()->asRiseFall() == rf) { + float from_slew = from_slews[arc->fromEdge()->index()]; + findInputArcDelay(drvr_pin, drvr_vertex, arc, from_slew, dcalc_ap); + } + } + } +} + +// Driving cell delay is the load dependent delay, which is the gate +// delay minus the intrinsic delay. Driving cell delays are annotated +// to the wire arcs from the input port pin to the load pins. +void +GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, + Vertex *drvr_vertex, + const TimingArc *arc, + float from_slew, + const DcalcAnalysisPt *dcalc_ap) +{ + debugPrint(debug_, "delay_calc", 3, " %s %s -> %s %s (%s)", + arc->from()->name(), + arc->fromEdge()->asString(), + arc->to()->name(), + arc->toEdge()->asString(), + arc->role()->asString()); + RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); + if (drvr_rf) { + DcalcAPIndex ap_index = dcalc_ap->index(); + const Pvt *pvt = dcalc_ap->operatingConditions(); + Parasitic *drvr_parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, + dcalc_ap); + float load_cap = loadCap(drvr_pin, drvr_parasitic, drvr_rf, dcalc_ap); + + ArcDelay intrinsic_delay; + Slew intrinsic_slew; + arc_delay_calc_->gateDelay(arc, Slew(from_slew), 0.0, 0, 0.0, pvt, dcalc_ap, + intrinsic_delay, intrinsic_slew); + + // For input drivers there is no instance to find a related_output_pin. + ArcDelay gate_delay; + Slew gate_slew; + arc_delay_calc_->gateDelay(arc, Slew(from_slew), load_cap, + drvr_parasitic, 0.0, pvt, dcalc_ap, + gate_delay, gate_slew); + ArcDelay load_delay = gate_delay - intrinsic_delay; + debugPrint(debug_, "delay_calc", 3, + " gate delay = %s intrinsic = %s slew = %s", + delayAsString(gate_delay, this), + delayAsString(intrinsic_delay, this), + delayAsString(gate_slew, this)); + graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); + annotateLoadDelays(drvr_vertex, drvr_rf, load_delay, false, dcalc_ap, + arc_delay_calc_); + } +} + +void +GraphDelayCalc::findDelays(Vertex *drvr_vertex) +{ + findVertexDelay(drvr_vertex, arc_delay_calc_, true); +} + +void +GraphDelayCalc::findVertexDelay(Vertex *vertex, + ArcDelayCalc *arc_delay_calc, + bool propagate) +{ + const Pin *pin = vertex->pin(); + debugPrint(debug_, "delay_calc", 2, "find delays %s (%s)", + vertex->name(sdc_network_), + network_->cellName(network_->instance(pin))); + if (vertex->isRoot()) { + seedRootSlew(vertex, arc_delay_calc); + if (propagate) + iter_->enqueueAdjacentVertices(vertex); + } + else { + if (network_->isLeaf(pin)) { + if (vertex->isDriver(network_)) { + bool delay_changed = findDriverDelays(vertex, arc_delay_calc); + if (propagate) { + if (network_->direction(pin)->isInternal()) + enqueueTimingChecksEdges(vertex); + // Enqueue adjacent vertices even if the delays did not + // change when non-incremental to stride past annotations. + if (delay_changed || !incremental_) + iter_->enqueueAdjacentVertices(vertex); + } + } + else { + // Load vertex. + enqueueTimingChecksEdges(vertex); + // Enqueue driver vertices from this input load. + if (propagate) + iter_->enqueueAdjacentVertices(vertex); + } + } + // Bidirect port drivers are enqueued by their load vertex in + // annotateLoadDelays. + else if (vertex->isBidirectDriver() + && network_->isTopLevelPort(pin)) + seedRootSlew(vertex, arc_delay_calc); + } +} + +void +GraphDelayCalc::enqueueTimingChecksEdges(Vertex *vertex) +{ + if (vertex->hasChecks()) { + VertexInEdgeIterator edge_iter(vertex, graph_); + UniqueLock lock(invalid_edge_lock_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->isTimingCheck()) + invalid_check_edges_.insert(edge); + } + } + if (vertex->isCheckClk()) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + UniqueLock lock(invalid_edge_lock_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->isTimingCheck()) + invalid_check_edges_.insert(edge); + } + } + if (network_->isLatchData(vertex->pin())) { + // Latch D->Q arcs have to be re-evaled if level(D) > level(E) + // because levelization does not traverse D->Q arcs to break loops. + VertexOutEdgeIterator edge_iter(vertex, graph_); + UniqueLock lock(invalid_edge_lock_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role() == TimingRole::latchDtoQ()) + invalid_latch_edges_.insert(edge); + } + } +} + +bool +GraphDelayCalc::findDriverDelays(Vertex *drvr_vertex, + ArcDelayCalc *arc_delay_calc) +{ + bool delay_changed = false; + MultiDrvrNet *multi_drvr = findMultiDrvrNet(drvr_vertex); + if (multi_drvr + && multi_drvr->parallelGates(network_)) { + Vertex *dcalc_drvr = multi_drvr->dcalcDrvr(); + if (drvr_vertex == dcalc_drvr) { + initLoadSlews(drvr_vertex); + arc_delay_calc->findParallelGateDelays(multi_drvr, this); + for (Vertex *drvr_vertex : *multi_drvr->drvrs()) + delay_changed |= findDriverDelays1(drvr_vertex, multi_drvr, arc_delay_calc); + } + } + else { + initLoadSlews(drvr_vertex); + delay_changed = findDriverDelays1(drvr_vertex, nullptr, arc_delay_calc); + } + arc_delay_calc->finishDrvrPin(); + return delay_changed; +} + +MultiDrvrNet * +GraphDelayCalc::findMultiDrvrNet(Vertex *drvr_vertex) +{ + MultiDrvrNet *multi_drvr = multiDrvrNet(drvr_vertex); + if (multi_drvr) + return multi_drvr; + else { + const PinSet *drvrs = network_->drivers(drvr_vertex->pin()); + if (drvrs && drvrs->size() > 1) { + PinSet drvrs1(network_); + // Filter input ports and non-leaf drivers. + for (const Pin *pin : *drvrs) { + if (isLeafDriver(pin, network_)) + drvrs1.insert(pin); + } + MultiDrvrNet *multi_drvr = nullptr; + if (drvrs1.size() > 1) + multi_drvr = makeMultiDrvrNet(drvrs1); + return multi_drvr; + } + else + return nullptr; + } +} + +static bool +isLeafDriver(const Pin *pin, + const Network *network) +{ + PortDirection *dir = network->direction(pin); + const Instance *inst = network->instance(pin); + return network->isLeaf(inst) && dir->isAnyOutput(); +} + +MultiDrvrNet * +GraphDelayCalc::multiDrvrNet(const Vertex *drvr_vertex) const +{ + return multi_drvr_net_map_.findKey(drvr_vertex); +} + +MultiDrvrNet * +GraphDelayCalc::makeMultiDrvrNet(PinSet &drvr_pins) +{ + debugPrint(debug_, "delay_calc", 3, "multi-driver net"); + VertexSet *drvr_vertices = new VertexSet(graph_); + MultiDrvrNet *multi_drvr = new MultiDrvrNet(drvr_vertices); + Level max_drvr_level = 0; + Vertex *max_drvr = nullptr; + PinSet::Iterator pin_iter(drvr_pins); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + Vertex *drvr_vertex = graph_->pinDrvrVertex(pin); + debugPrint(debug_, "delay_calc", 3, " %s", + network_->pathName(pin)); + multi_drvr_net_map_[drvr_vertex] = multi_drvr; + drvr_vertices->insert(drvr_vertex); + Level drvr_level = drvr_vertex->level(); + if (max_drvr == nullptr + || drvr_level > max_drvr_level) { + max_drvr = drvr_vertex; + max_drvr_level = drvr_level; + } + } + multi_drvr->setDcalcDrvr(max_drvr); + multi_drvr->findCaps(sdc_); + return multi_drvr; +} + +void +GraphDelayCalc::initLoadSlews(Vertex *drvr_vertex) +{ + VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *wire_edge = edge_iter.next(); + if (wire_edge->isWire()) { + Vertex *load_vertex = wire_edge->to(graph_); + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + Slew slew_init_value(slew_min_max->initValue()); + DcalcAPIndex ap_index = dcalc_ap->index(); + for (auto rf : RiseFall::range()) { + if (!load_vertex->slewAnnotated(rf, slew_min_max)) + graph_->setSlew(load_vertex, rf, ap_index, slew_init_value); + } + } + } + } +} + +bool +GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex, + MultiDrvrNet *multi_drvr, + ArcDelayCalc *arc_delay_calc) +{ + const Pin *drvr_pin = drvr_vertex->pin(); + Instance *drvr_inst = network_->instance(drvr_pin); + initSlew(drvr_vertex); + initWireDelays(drvr_vertex); + bool delay_changed = false; + bool has_delays = false; + VertexInEdgeIterator edge_iter(drvr_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + // Don't let disabled edges set slews that influence downstream delays. + if (search_pred_->searchFrom(from_vertex) + && search_pred_->searchThru(edge) + && !edge->role()->isLatchDtoQ()) { + delay_changed |= findDriverEdgeDelays(drvr_inst, drvr_pin, drvr_vertex, + multi_drvr, edge, arc_delay_calc); + has_delays = true; + } + } + if (!has_delays) + zeroSlewAndWireDelays(drvr_vertex); + if (delay_changed && observer_) + observer_->delayChangedTo(drvr_vertex); + return delay_changed; +} + +// Init slews to zero on root vertices that are not inputs, such as +// floating input pins. +void +GraphDelayCalc::initRootSlews(Vertex *vertex) +{ + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + DcalcAPIndex ap_index = dcalc_ap->index(); + for (auto rf : RiseFall::range()) { + if (!vertex->slewAnnotated(rf, slew_min_max)) + graph_->setSlew(vertex, rf, ap_index, default_slew); + } + } +} + +void +GraphDelayCalc::findLatchEdgeDelays(Edge *edge) +{ + Vertex *drvr_vertex = edge->to(graph_); + const Pin *drvr_pin = drvr_vertex->pin(); + Instance *drvr_inst = network_->instance(drvr_pin); + debugPrint(debug_, "delay_calc", 2, "find latch D->Q %s", + sdc_network_->pathName(drvr_inst)); + bool delay_changed = findDriverEdgeDelays(drvr_inst, drvr_pin, drvr_vertex, + nullptr, edge, arc_delay_calc_); + if (delay_changed && observer_) + observer_->delayChangedTo(drvr_vertex); +} + +bool +GraphDelayCalc::findDriverEdgeDelays(const Instance *drvr_inst, + const Pin *drvr_pin, + Vertex *drvr_vertex, + const MultiDrvrNet *multi_drvr, + Edge *edge, + ArcDelayCalc *arc_delay_calc) +{ + Vertex *in_vertex = edge->from(graph_); + const TimingArcSet *arc_set = edge->timingArcSet(); + const LibertyPort *related_out_port = arc_set->relatedOut(); + const Pin *related_out_pin = 0; + bool delay_changed = false; + if (related_out_port) + related_out_pin = network_->findPin(drvr_inst, related_out_port); + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + const Pvt *pvt = sdc_->pvt(drvr_inst, dcalc_ap->constraintMinMax()); + if (pvt == nullptr) + pvt = dcalc_ap->operatingConditions(); + for (TimingArc *arc : arc_set->arcs()) { + const RiseFall *rf = arc->toEdge()->asRiseFall(); + Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, dcalc_ap); + float related_out_cap = 0.0; + if (related_out_pin) { + Parasitic *related_out_parasitic = + arc_delay_calc->findParasitic(related_out_pin, rf, dcalc_ap); + related_out_cap = loadCap(related_out_pin, related_out_parasitic, rf, dcalc_ap); + } + delay_changed |= findArcDelay(drvr_pin, drvr_vertex, arc, parasitic, + related_out_cap, in_vertex, edge, pvt, dcalc_ap, + multi_drvr, arc_delay_calc); + } + } + + if (delay_changed && observer_) { + observer_->delayChangedFrom(in_vertex); + observer_->delayChangedFrom(drvr_vertex); + } + return delay_changed; } float -GraphDelayCalc::ceff(Edge *, - TimingArc *, - const DcalcAnalysisPt *) +GraphDelayCalc::loadCap(const Pin *drvr_pin, + const DcalcAnalysisPt *dcalc_ap) const { - return 0.0; + const MinMax *min_max = dcalc_ap->constraintMinMax(); + float load_cap = 0.0; + for (auto drvr_rf : RiseFall::range()) { + Parasitic *drvr_parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, + dcalc_ap); + float cap = loadCap(drvr_pin, drvr_parasitic, drvr_rf, dcalc_ap, nullptr); + arc_delay_calc_->finishDrvrPin(); + if (min_max->compare(cap, load_cap)) + load_cap = cap; + } + return load_cap; } +float +GraphDelayCalc::loadCap(const Pin *drvr_pin, + const RiseFall *drvr_rf, + const DcalcAnalysisPt *dcalc_ap) const +{ + Parasitic *drvr_parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, + dcalc_ap); + float cap = loadCap(drvr_pin, drvr_parasitic, drvr_rf, dcalc_ap, nullptr); + return cap; +} + +float +GraphDelayCalc::loadCap(const Pin *drvr_pin, + const Parasitic *drvr_parasitic, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap) const +{ + return loadCap(drvr_pin, drvr_parasitic, rf, dcalc_ap, nullptr); +} + +float +GraphDelayCalc::loadCap(const Pin *drvr_pin, + const Parasitic *drvr_parasitic, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap, + const MultiDrvrNet *multi_drvr) const +{ + float pin_cap, wire_cap; + bool has_net_load; + float fanout; + if (multi_drvr) + multi_drvr->netCaps(rf, dcalc_ap, + pin_cap, wire_cap, fanout, has_net_load); + else + netCaps(drvr_pin, rf, dcalc_ap, + pin_cap, wire_cap, fanout, has_net_load); + loadCap(drvr_parasitic, has_net_load, pin_cap, wire_cap); + return wire_cap + pin_cap; +} + +void +GraphDelayCalc::loadCap(const Pin *drvr_pin, + const Parasitic *drvr_parasitic, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap) const +{ + bool has_net_load; + float fanout; + // Find pin and external pin/wire capacitance. + netCaps(drvr_pin, rf, dcalc_ap, + pin_cap, wire_cap, fanout, has_net_load); + loadCap(drvr_parasitic, has_net_load, pin_cap, wire_cap); +} + +void +GraphDelayCalc::loadCap(const Parasitic *drvr_parasitic, + bool has_net_load, + // Return values. + float &pin_cap, + float &wire_cap) const +{ + // set_load net has precidence over parasitics. + if (!has_net_load && drvr_parasitic) { + if (parasitics_->isParasiticNetwork(drvr_parasitic)) + wire_cap += parasitics_->capacitance(drvr_parasitic); + else { + // PiModel includes both pin and external caps. + float cap = parasitics_->capacitance(drvr_parasitic); + if (pin_cap > cap) { + pin_cap = 0.0; + wire_cap = cap; + } + else + wire_cap = cap - pin_cap; + } + } +} + +void +GraphDelayCalc::netCaps(const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load) const +{ + MultiDrvrNet *multi_drvr = nullptr; + if (graph_) { + Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin); + multi_drvr = multiDrvrNet(drvr_vertex); + } + if (multi_drvr) + multi_drvr->netCaps(rf, dcalc_ap, + pin_cap, wire_cap, fanout, has_net_load); + else { + const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); + const Corner *corner = dcalc_ap->corner(); + const MinMax *min_max = dcalc_ap->constraintMinMax(); + // Find pin and external pin/wire capacitance. + sdc_->connectedCap(drvr_pin, rf, op_cond, corner, min_max, + pin_cap, wire_cap, fanout, has_net_load); + } +} + +void +GraphDelayCalc::initSlew(Vertex *vertex) +{ + for (auto rf : RiseFall::range()) { + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + if (!vertex->slewAnnotated(rf, slew_min_max)) { + DcalcAPIndex ap_index = dcalc_ap->index(); + graph_->setSlew(vertex, rf, ap_index, slew_min_max->initValue()); + } + } + } +} + +void +GraphDelayCalc::zeroSlewAndWireDelays(Vertex *drvr_vertex) +{ + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + DcalcAPIndex ap_index = dcalc_ap->index(); + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + for (auto rf : RiseFall::range()) { + // Init drvr slew. + if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) { + DcalcAPIndex ap_index = dcalc_ap->index(); + graph_->setSlew(drvr_vertex, rf, ap_index, slew_min_max->initValue()); + } + + // Init wire delays and slews. + VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *wire_edge = edge_iter.next(); + if (wire_edge->isWire()) { + Vertex *load_vertex = wire_edge->to(graph_); + if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) + graph_->setWireArcDelay(wire_edge, rf, ap_index, 0.0); + // Init load vertex slew. + if (!load_vertex->slewAnnotated(rf, slew_min_max)) + graph_->setSlew(load_vertex, rf, ap_index, 0.0); + } + } + } + } +} + +void +GraphDelayCalc::initWireDelays(Vertex *drvr_vertex) +{ + VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *wire_edge = edge_iter.next(); + if (wire_edge->isWire()) { + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + const MinMax *delay_min_max = dcalc_ap->delayMinMax(); + Delay delay_init_value(delay_min_max->initValue()); + DcalcAPIndex ap_index = dcalc_ap->index(); + for (auto rf : RiseFall::range()) { + if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) + graph_->setWireArcDelay(wire_edge, rf, ap_index, delay_init_value); + } + } + } + } +} + +// Call the arc delay calculator to find the delay thru a single gate +// input to output timing arc, The wire delays from the gate output to +// each load pin, and the slew at each load pin. Annotate the graph +// with the results. +bool +GraphDelayCalc::findArcDelay(const Pin *drvr_pin, + Vertex *drvr_vertex, + const TimingArc *arc, + const Parasitic *drvr_parasitic, + float related_out_cap, + Vertex *from_vertex, + Edge *edge, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + const MultiDrvrNet *multi_drvr, + ArcDelayCalc *arc_delay_calc) +{ + bool delay_changed = false; + RiseFall *from_rf = arc->fromEdge()->asRiseFall(); + RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); + if (from_rf && drvr_rf) { + DcalcAPIndex ap_index = dcalc_ap->index(); + debugPrint(debug_, "delay_calc", 3, + " %s %s -> %s %s (%s) corner:%s/%s", + arc->from()->name(), + arc->fromEdge()->asString(), + arc->to()->name(), + arc->toEdge()->asString(), + arc->role()->asString(), + dcalc_ap->corner()->name(), + dcalc_ap->delayMinMax()->asString()); + // Delay calculation is done even when the gate delays/slews are + // annotated because the wire delays may not be annotated. + const Slew from_slew = edgeFromSlew(from_vertex, from_rf, edge, dcalc_ap); + ArcDelay gate_delay; + Slew gate_slew; + float load_cap = loadCap(drvr_pin, drvr_parasitic, drvr_rf, dcalc_ap, multi_drvr); + if (multi_drvr + && multi_drvr->parallelGates(network_)) + arc_delay_calc->parallelGateDelay(drvr_pin, arc, from_slew, load_cap, + drvr_parasitic, related_out_cap, pvt, dcalc_ap, + gate_delay, gate_slew); + else + arc_delay_calc->gateDelay(arc, from_slew, load_cap, drvr_parasitic, + related_out_cap, pvt, dcalc_ap, + gate_delay, gate_slew); + debugPrint(debug_, "delay_calc", 3, + " gate delay = %s slew = %s", + delayAsString(gate_delay, this), + delayAsString(gate_slew, this)); + // Merge slews. + const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + if (delayGreater(gate_slew, drvr_slew, dcalc_ap->slewMinMax(), this) + && !drvr_vertex->slewAnnotated(drvr_rf, slew_min_max) + && !edge->role()->isLatchDtoQ()) + graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); + if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { + const ArcDelay &prev_gate_delay = graph_->arcDelay(edge,arc,ap_index); + float gate_delay1 = delayAsFloat(gate_delay); + float prev_gate_delay1 = delayAsFloat(prev_gate_delay); + if (prev_gate_delay1 == 0.0 + || (abs(gate_delay1 - prev_gate_delay1) / prev_gate_delay1 + > incremental_delay_tolerance_)) + delay_changed = true; + graph_->setArcDelay(edge, arc, ap_index, gate_delay); + } + if (!edge->role()->isLatchDtoQ()) + annotateLoadDelays(drvr_vertex, drvr_rf, delay_zero, true, dcalc_ap, + arc_delay_calc); + } + return delay_changed; +} + +// Use clock slew for register/latch clk->q edges. +Slew +GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex, + const RiseFall *from_rf, + const Edge *edge, + const DcalcAnalysisPt *dcalc_ap) +{ + const TimingRole *role = edge->role(); + if (role->genericRole() == TimingRole::regClkToQ() + && clk_network_->isIdealClock(from_vertex->pin())) + return clk_network_->idealClkSlew(from_vertex->pin(), from_rf, + dcalc_ap->slewMinMax()); + else + return graph_->slew(from_vertex, from_rf, dcalc_ap->index()); +} + +// Annotate wire arc delays and load pin slews. +// extra_delay is additional wire delay to add to delay returned +// by the delay calculator. +void +GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, + const RiseFall *drvr_rf, + const ArcDelay &extra_delay, + bool merge, + const DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc) +{ + DcalcAPIndex ap_index = dcalc_ap->index(); + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *wire_edge = edge_iter.next(); + if (wire_edge->isWire()) { + Vertex *load_vertex = wire_edge->to(graph_); + Pin *load_pin = load_vertex->pin(); + ArcDelay wire_delay; + Slew load_slew; + arc_delay_calc->loadDelay(load_pin, wire_delay, load_slew); + debugPrint(debug_, "delay_calc", 3, + " %s load delay = %s slew = %s", + load_vertex->name(sdc_network_), + delayAsString(wire_delay, this), + delayAsString(load_slew, this)); + if (!load_vertex->slewAnnotated(drvr_rf, slew_min_max)) { + if (drvr_vertex->slewAnnotated(drvr_rf, slew_min_max)) { + // Copy the driver slew to the load if it is annotated. + const Slew &drvr_slew = graph_->slew(drvr_vertex,drvr_rf,ap_index); + graph_->setSlew(load_vertex, drvr_rf, ap_index, drvr_slew); + } + else { + const Slew &slew = graph_->slew(load_vertex, drvr_rf, ap_index); + if (!merge + || delayGreater(load_slew, slew, slew_min_max, this)) + graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew); + } + } + if (!graph_->wireDelayAnnotated(wire_edge, drvr_rf, ap_index)) { + // Multiple timing arcs with the same output transition + // annotate the same wire edges so they must be combined + // rather than set. + const ArcDelay &delay = graph_->wireArcDelay(wire_edge, drvr_rf, + ap_index); + Delay wire_delay_extra = extra_delay + wire_delay; + const MinMax *delay_min_max = dcalc_ap->delayMinMax(); + if (!merge + || delayGreater(wire_delay_extra, delay, delay_min_max, this)) { + graph_->setWireArcDelay(wire_edge, drvr_rf, ap_index, + wire_delay_extra); + if (observer_) + observer_->delayChangedTo(load_vertex); + } + } + // Enqueue bidirect driver from load vertex. + if (sdc_->bidirectDrvrSlewFromLoad(load_pin)) + iter_->enqueue(graph_->pinDrvrVertex(load_pin)); + } + } +} + +void +GraphDelayCalc::findCheckEdgeDelays(Edge *edge, + ArcDelayCalc *arc_delay_calc) +{ + Vertex *from_vertex = edge->from(graph_); + Vertex *to_vertex = edge->to(graph_); + TimingArcSet *arc_set = edge->timingArcSet(); + const Pin *to_pin = to_vertex->pin(); + Instance *inst = network_->instance(to_pin); + debugPrint(debug_, "delay_calc", 2, "find check %s %s -> %s", + sdc_network_->pathName(inst), + network_->portName(from_vertex->pin()), + network_->portName(to_pin)); + bool delay_changed = false; + for (TimingArc *arc : arc_set->arcs()) { + RiseFall *from_rf = arc->fromEdge()->asRiseFall(); + RiseFall *to_rf = arc->toEdge()->asRiseFall(); + if (from_rf && to_rf) { + const LibertyPort *related_out_port = arc_set->relatedOut(); + const Pin *related_out_pin = 0; + if (related_out_port) + related_out_pin = network_->findPin(inst, related_out_port); + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + DcalcAPIndex ap_index = dcalc_ap->index(); + if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { + const Pvt *pvt = sdc_->pvt(inst,dcalc_ap->constraintMinMax()); + if (pvt == nullptr) + pvt = dcalc_ap->operatingConditions(); + const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, + dcalc_ap); + int slew_index = dcalc_ap->checkDataSlewIndex(); + const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index); + debugPrint(debug_, "delay_calc", 3, + " %s %s -> %s %s (%s)", + arc_set->from()->name(), + arc->fromEdge()->asString(), + arc_set->to()->name(), + arc->toEdge()->asString(), + arc_set->role()->asString()); + debugPrint(debug_, "delay_calc", 3, + " from_slew = %s to_slew = %s", + delayAsString(from_slew, this), + delayAsString(to_slew, this)); + float related_out_cap = 0.0; + if (related_out_pin) { + Parasitic *related_out_parasitic = + arc_delay_calc->findParasitic(related_out_pin, to_rf, dcalc_ap); + related_out_cap = loadCap(related_out_pin, + related_out_parasitic, + to_rf, dcalc_ap); + } + ArcDelay check_delay; + arc_delay_calc->checkDelay(arc, from_slew, to_slew, related_out_cap, + pvt, dcalc_ap, check_delay); + debugPrint(debug_, "delay_calc", 3, + " check_delay = %s", + delayAsString(check_delay, this)); + graph_->setArcDelay(edge, arc, ap_index, check_delay); + delay_changed = true; + } + } + } + } + + if (delay_changed && observer_) + observer_->checkDelayChangedTo(to_vertex); +} + +// Use clock slew for timing check clock edges. +Slew +GraphDelayCalc::checkEdgeClkSlew(const Vertex *from_vertex, + const RiseFall *from_rf, + const DcalcAnalysisPt *dcalc_ap) +{ + if (clk_network_->isIdealClock(from_vertex->pin())) + return clk_network_->idealClkSlew(from_vertex->pin(), from_rf, + dcalc_ap->checkClkSlewMinMax()); + else + return graph_->slew(from_vertex, from_rf, dcalc_ap->checkClkSlewIndex()); +} + +//////////////////////////////////////////////////////////////// + +float +GraphDelayCalc::ceff(Edge *edge, + TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap) +{ + Vertex *from_vertex = edge->from(graph_); + Vertex *to_vertex = edge->to(graph_); + Pin *to_pin = to_vertex->pin(); + Instance *inst = network_->instance(to_pin); + const TimingArcSet *arc_set = edge->timingArcSet(); + float ceff = 0.0; + const Pvt *pvt = sdc_->pvt(inst, dcalc_ap->constraintMinMax()); + if (pvt == nullptr) + pvt = dcalc_ap->operatingConditions(); + RiseFall *from_rf = arc->fromEdge()->asRiseFall(); + RiseFall *to_rf = arc->toEdge()->asRiseFall(); + if (from_rf && to_rf) { + const LibertyPort *related_out_port = arc_set->relatedOut(); + const Pin *related_out_pin = 0; + if (related_out_port) + related_out_pin = network_->findPin(inst, related_out_port); + float related_out_cap = 0.0; + if (related_out_pin) { + Parasitic *related_out_parasitic = + arc_delay_calc_->findParasitic(related_out_pin, to_rf, dcalc_ap); + related_out_cap = loadCap(related_out_pin, related_out_parasitic, + to_rf, dcalc_ap); + } + Parasitic *to_parasitic = arc_delay_calc_->findParasitic(to_pin, to_rf, + dcalc_ap); + const Slew &from_slew = edgeFromSlew(from_vertex, from_rf, edge, dcalc_ap); + float load_cap = loadCap(to_pin, to_parasitic, to_rf, dcalc_ap); + ceff = arc_delay_calc_->ceff(arc, from_slew, load_cap, to_parasitic, + related_out_cap, pvt, dcalc_ap); + arc_delay_calc_->finishDrvrPin(); + } + return ceff; +} + +//////////////////////////////////////////////////////////////// + +string +GraphDelayCalc::reportDelayCalc(const Edge *edge, + const TimingArc *arc, + const Corner *corner, + const MinMax *min_max, + int digits) +{ + Vertex *from_vertex = edge->from(graph_); + Vertex *to_vertex = edge->to(graph_); + Pin *to_pin = to_vertex->pin(); + TimingRole *role = arc->role(); + const Instance *inst = network_->instance(to_pin); + const TimingArcSet *arc_set = edge->timingArcSet(); + string result; + DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + const Pvt *pvt = sdc_->pvt(inst, dcalc_ap->constraintMinMax()); + if (pvt == nullptr) + pvt = dcalc_ap->operatingConditions(); + RiseFall *from_rf = arc->fromEdge()->asRiseFall(); + RiseFall *to_rf = arc->toEdge()->asRiseFall(); + if (from_rf && to_rf) { + const LibertyPort *related_out_port = arc_set->relatedOut(); + const Pin *related_out_pin = 0; + if (related_out_port) + related_out_pin = network_->findPin(inst, related_out_port); + float related_out_cap = 0.0; + if (related_out_pin) { + Parasitic *related_out_parasitic = + arc_delay_calc_->findParasitic(related_out_pin, to_rf, dcalc_ap); + related_out_cap = loadCap(related_out_pin, related_out_parasitic, + to_rf, dcalc_ap); + } + if (role->isTimingCheck()) { + const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, dcalc_ap); + int slew_index = dcalc_ap->checkDataSlewIndex(); + const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index); + bool from_ideal_clk = clk_network_->isIdealClock(from_vertex->pin()); + const char *from_slew_annotation = from_ideal_clk ? " (ideal clock)" : nullptr; + result = arc_delay_calc_->reportCheckDelay(arc, from_slew, from_slew_annotation, + to_slew, related_out_cap, pvt, + dcalc_ap, digits); + } + else { + Parasitic *to_parasitic = + arc_delay_calc_->findParasitic(to_pin, to_rf, dcalc_ap); + const Slew &from_slew = edgeFromSlew(from_vertex, from_rf, edge, dcalc_ap); + float load_cap = loadCap(to_pin, to_parasitic, to_rf, dcalc_ap); + result = arc_delay_calc_->reportGateDelay(arc, from_slew, load_cap, to_parasitic, + related_out_cap, pvt, dcalc_ap, digits); + } + arc_delay_calc_->finishDrvrPin(); + } + return result; +} + +//////////////////////////////////////////////////////////////// + void GraphDelayCalc::minPulseWidth(const Pin *pin, const RiseFall *hi_low, @@ -166,4 +1422,73 @@ GraphDelayCalc::minPeriod(const Pin *pin, } } +//////////////////////////////////////////////////////////////// + +MultiDrvrNet::MultiDrvrNet(VertexSet *drvrs) : + dcalc_drvr_(nullptr), + drvrs_(drvrs) +{ +} + +MultiDrvrNet::~MultiDrvrNet() +{ + delete drvrs_; +} + +void +MultiDrvrNet::netCaps(const RiseFall *drvr_rf, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load) const +{ + int index = dcalc_ap->index() * RiseFall::index_count + + drvr_rf->index(); + const NetCaps &net_caps = net_caps_[index]; + pin_cap = net_caps.pinCap(); + wire_cap = net_caps.wireCap(); + fanout = net_caps.fanout(); + has_net_load = net_caps.hasNetLoad(); +} + +void +MultiDrvrNet::findCaps(const Sdc *sdc) +{ + Corners *corners = sdc->corners(); + int count = RiseFall::index_count * corners->dcalcAnalysisPtCount(); + net_caps_.resize(count); + const Pin *drvr_pin = dcalc_drvr_->pin(); + for (auto dcalc_ap : corners->dcalcAnalysisPts()) { + DcalcAPIndex ap_index = dcalc_ap->index(); + const Corner *corner = dcalc_ap->corner(); + const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); + const MinMax *min_max = dcalc_ap->constraintMinMax(); + for (auto drvr_rf : RiseFall::range()) { + int drvr_rf_index = drvr_rf->index(); + int index = ap_index * RiseFall::index_count + drvr_rf_index; + NetCaps &net_caps = net_caps_[index]; + float pin_cap, wire_cap, fanout; + bool has_net_load; + // Find pin and external pin/wire capacitance. + sdc->connectedCap(drvr_pin, drvr_rf, op_cond, corner, min_max, + pin_cap, wire_cap, fanout, has_net_load); + net_caps.init(pin_cap, wire_cap, fanout, has_net_load); + } + } +} + +void +MultiDrvrNet::setDcalcDrvr(Vertex *drvr) +{ + dcalc_drvr_ = drvr; +} + +bool +MultiDrvrNet::parallelGates(const Network *network) const +{ + return network->direction(dcalc_drvr_->pin())->isOutput(); +} + } // namespace diff --git a/dcalc/GraphDelayCalc1.cc b/dcalc/GraphDelayCalc1.cc deleted file mode 100644 index 53566b45..00000000 --- a/dcalc/GraphDelayCalc1.cc +++ /dev/null @@ -1,1690 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2023, Parallax Software, Inc. -// -// 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 . - -#include "GraphDelayCalc1.hh" - -#include "Debug.hh" -#include "Stats.hh" -#include "MinMax.hh" -#include "Mutex.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "PortDirection.hh" -#include "Network.hh" -#include "InputDrive.hh" -#include "Sdc.hh" -#include "Graph.hh" -#include "Parasitics.hh" -#include "search/Levelize.hh" -#include "Corner.hh" -#include "SearchPred.hh" -#include "Bfs.hh" -#include "ArcDelayCalc.hh" -#include "DcalcAnalysisPt.hh" -#include "NetCaps.hh" -#include "ClkNetwork.hh" - -namespace sta { - -using std::abs; - -static const Slew default_slew = 0.0; - -typedef Set MultiDrvrNetSet; - -static bool -isLeafDriver(const Pin *pin, - const Network *network); - -// Cache parallel delay/slew values for nets with multiple drivers. -class MultiDrvrNet -{ -public: - MultiDrvrNet(VertexSet *drvrs); - ~MultiDrvrNet(); - const VertexSet *drvrs() const { return drvrs_; } - VertexSet *drvrs() { return drvrs_; } - Vertex *dcalcDrvr() const { return dcalc_drvr_; } - void setDcalcDrvr(Vertex *drvr); - void parallelDelaySlew(const RiseFall *drvr_rf, - const DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc, - GraphDelayCalc1 *dcalc, - // Return values. - ArcDelay ¶llel_delay, - Slew ¶llel_slew); - void netCaps(const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_net_load); - void findCaps(const GraphDelayCalc1 *dcalc, - const Sdc *sdc); - -private: - void findDelaysSlews(ArcDelayCalc *arc_delay_calc, - GraphDelayCalc1 *dcalc); - - // Driver that triggers delay calculation for all the drivers on the net. - Vertex *dcalc_drvr_; - VertexSet *drvrs_; - // [drvr_rf->index][dcalc_ap->index] - ArcDelay *parallel_delays_; - // [drvr_rf->index][dcalc_ap->index] - Slew *parallel_slews_; - // [drvr_rf->index][dcalc_ap->index] - NetCaps *net_caps_; - bool delays_valid_:1; -}; - -MultiDrvrNet::MultiDrvrNet(VertexSet *drvrs) : - dcalc_drvr_(nullptr), - drvrs_(drvrs), - parallel_delays_(nullptr), - parallel_slews_(nullptr), - net_caps_(nullptr), - delays_valid_(false) -{ -} - -MultiDrvrNet::~MultiDrvrNet() -{ - delete drvrs_; - if (delays_valid_) { - delete [] parallel_delays_; - delete [] parallel_slews_; - } - delete [] net_caps_; -} - -void -MultiDrvrNet::parallelDelaySlew(const RiseFall *drvr_rf, - const DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc, - GraphDelayCalc1 *dcalc, - // Return values. - ArcDelay ¶llel_delay, - Slew ¶llel_slew) -{ - if (!delays_valid_) { - findDelaysSlews(arc_delay_calc, dcalc); - delays_valid_ = true; - } - - int index = dcalc_ap->index() * RiseFall::index_count - + drvr_rf->index(); - parallel_delay = parallel_delays_[index]; - parallel_slew = parallel_slews_[index]; -} - -void -MultiDrvrNet::findDelaysSlews(ArcDelayCalc *arc_delay_calc, - GraphDelayCalc1 *dcalc) -{ - Corners *corners = dcalc->corners(); - int count = RiseFall::index_count * corners->dcalcAnalysisPtCount(); - parallel_delays_ = new ArcDelay[count]; - parallel_slews_ = new Slew[count]; - for (auto dcalc_ap : corners->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); - const Pvt *pvt = dcalc_ap->operatingConditions(); - for (auto drvr_rf : RiseFall::range()) { - int drvr_rf_index = drvr_rf->index(); - int index = ap_index*RiseFall::index_count+drvr_rf_index; - dcalc->findMultiDrvrGateDelay(this, drvr_rf, pvt, dcalc_ap, - arc_delay_calc, - parallel_delays_[index], - parallel_slews_[index]); - } - } -} - -void -MultiDrvrNet::netCaps(const RiseFall *drvr_rf, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_net_load) -{ - int index = dcalc_ap->index() * RiseFall::index_count - + drvr_rf->index(); - NetCaps &net_caps = net_caps_[index]; - pin_cap = net_caps.pinCap(); - wire_cap = net_caps.wireCap(); - fanout = net_caps.fanout(); - has_net_load = net_caps.hasNetLoad(); -} - -void -MultiDrvrNet::findCaps(const GraphDelayCalc1 *dcalc, - const Sdc *sdc) -{ - Corners *corners = dcalc->corners(); - int count = RiseFall::index_count * corners->dcalcAnalysisPtCount(); - net_caps_ = new NetCaps[count]; - const Pin *drvr_pin = dcalc_drvr_->pin(); - for (auto dcalc_ap : corners->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); - const Corner *corner = dcalc_ap->corner(); - const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); - const MinMax *min_max = dcalc_ap->constraintMinMax(); - for (auto drvr_rf : RiseFall::range()) { - int drvr_rf_index = drvr_rf->index(); - int index = ap_index * RiseFall::index_count + drvr_rf_index; - NetCaps &net_caps = net_caps_[index]; - float pin_cap, wire_cap, fanout; - bool has_net_load; - // Find pin and external pin/wire capacitance. - sdc->connectedCap(drvr_pin, drvr_rf, op_cond, corner, min_max, - pin_cap, wire_cap, fanout, has_net_load); - net_caps.init(pin_cap, wire_cap, fanout, has_net_load); - } - } -} - -void -MultiDrvrNet::setDcalcDrvr(Vertex *drvr) -{ - dcalc_drvr_ = drvr; -} - -//////////////////////////////////////////////////////////////// - - -GraphDelayCalc1::GraphDelayCalc1(StaState *sta) : - GraphDelayCalc(sta), - observer_(nullptr), - delays_seeded_(false), - incremental_(false), - delays_exist_(false), - invalid_delays_(new VertexSet(graph_)), - search_pred_(new SearchPred1(sta)), - search_non_latch_pred_(new SearchPredNonLatch2(sta)), - clk_pred_(new ClkTreeSearchPred(sta)), - iter_(new BfsFwdIterator(BfsIndex::dcalc, search_non_latch_pred_, sta)), - multi_drvr_nets_found_(false), - incremental_delay_tolerance_(0.0) -{ -} - -GraphDelayCalc1::~GraphDelayCalc1() -{ - delete search_pred_; - delete invalid_delays_; - delete search_non_latch_pred_; - delete clk_pred_; - delete iter_; - deleteMultiDrvrNets(); - delete observer_; -} - -void -GraphDelayCalc1::deleteMultiDrvrNets() -{ - MultiDrvrNetSet drvr_nets; - MultiDrvrNetMap::Iterator multi_iter(multi_drvr_net_map_); - while (multi_iter.hasNext()) { - MultiDrvrNet *multi_drvr = multi_iter.next(); - // Multiple drvr pins point to the same drvr PinSet, - // so collect them into a set. - drvr_nets.insert(multi_drvr); - } - multi_drvr_net_map_.clear(); - drvr_nets.deleteContents(); -} - -void -GraphDelayCalc1::copyState(const StaState *sta) -{ - GraphDelayCalc::copyState(sta); - // Notify sub-components. - iter_->copyState(sta); -} - -void -GraphDelayCalc1::clear() -{ - delaysInvalid(); - deleteMultiDrvrNets(); - multi_drvr_nets_found_ = false; - GraphDelayCalc::clear(); -} - -float -GraphDelayCalc1::incrementalDelayTolerance() -{ - return incremental_delay_tolerance_; -} - -void -GraphDelayCalc1::setIncrementalDelayTolerance(float tol) -{ - incremental_delay_tolerance_ = tol; -} - -void -GraphDelayCalc1::setObserver(DelayCalcObserver *observer) -{ - delete observer_; - observer_ = observer; -} - -void -GraphDelayCalc1::delaysInvalid() -{ - debugPrint(debug_, "delay_calc", 1, "delays invalid"); - delays_exist_ = false; - delays_seeded_ = false; - incremental_ = false; - iter_->clear(); - // No need to keep track of incremental updates any more. - invalid_delays_->clear(); - invalid_check_edges_.clear(); - invalid_latch_edges_.clear(); -} - -void -GraphDelayCalc1::delayInvalid(const Pin *pin) -{ - if (graph_ && incremental_) { - if (network_->isHierarchical(pin)) { - EdgesThruHierPinIterator edge_iter(pin, network_, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - delayInvalid(edge->from(graph_)); - } - } - else { - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - if (vertex) - delayInvalid(vertex); - if (bidirect_drvr_vertex) - delayInvalid(bidirect_drvr_vertex); - } - } -} - -void -GraphDelayCalc1::delayInvalid(Vertex *vertex) -{ - debugPrint(debug_, "delay_calc", 2, "delay invalid %s", - vertex->name(sdc_network_)); - if (graph_ && incremental_) { - invalid_delays_->insert(vertex); - // Invalidate driver that triggers dcalc for multi-driver nets. - MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); - if (multi_drvr) - invalid_delays_->insert(multi_drvr->dcalcDrvr()); - } -} - -void -GraphDelayCalc1::deleteVertexBefore(Vertex *vertex) -{ - iter_->deleteVertexBefore(vertex); - if (incremental_) - invalid_delays_->erase(vertex); - MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); - if (multi_drvr) { - multi_drvr->drvrs()->erase(vertex); - multi_drvr_net_map_.erase(vertex); - } -} - -//////////////////////////////////////////////////////////////// - -class FindVertexDelays : public VertexVisitor -{ -public: - FindVertexDelays(GraphDelayCalc1 *graph_delay_calc1); - virtual ~FindVertexDelays(); - virtual void visit(Vertex *vertex); - virtual VertexVisitor *copy() const; - -protected: - GraphDelayCalc1 *graph_delay_calc1_; - ArcDelayCalc *arc_delay_calc_; -}; - -FindVertexDelays::FindVertexDelays(GraphDelayCalc1 *graph_delay_calc1) : - VertexVisitor(), - graph_delay_calc1_(graph_delay_calc1), - arc_delay_calc_(graph_delay_calc1_->arc_delay_calc_->copy()) -{ -} - -FindVertexDelays::~FindVertexDelays() -{ - delete arc_delay_calc_; -} - -VertexVisitor * -FindVertexDelays::copy() const -{ - // Copy StaState::arc_delay_calc_ because it needs separate state - // for each thread. - return new FindVertexDelays(graph_delay_calc1_); -} - -void -FindVertexDelays::visit(Vertex *vertex) -{ - graph_delay_calc1_->findVertexDelay(vertex, arc_delay_calc_, true); -} - -// The logical structure of incremental delay calculation closely -// resembles the incremental search arrival time algorithm -// (Search::findArrivals). -void -GraphDelayCalc1::findDelays(Level level) -{ - if (arc_delay_calc_) { - Stats stats(debug_, report_); - int dcalc_count = 0; - debugPrint(debug_, "delay_calc", 1, "find delays to level %d", level); - if (!delays_seeded_) { - iter_->clear(); - ensureMultiDrvrNetsFound(); - seedRootSlews(); - delays_seeded_ = true; - } - else - iter_->ensureSize(); - if (incremental_) - seedInvalidDelays(); - - FindVertexDelays visitor(this); - dcalc_count += iter_->visitParallel(level, &visitor); - - // Timing checks require slews at both ends of the arc, - // so find their delays after all slews are known. - for (Edge *check_edge : invalid_check_edges_) - findCheckEdgeDelays(check_edge, arc_delay_calc_); - invalid_check_edges_.clear(); - - for (Edge *latch_edge : invalid_latch_edges_) - findLatchEdgeDelays(latch_edge); - invalid_latch_edges_.clear(); - - delays_exist_ = true; - incremental_ = true; - debugPrint(debug_, "delay_calc", 1, "found %d delays", dcalc_count); - stats.report("Delay calc"); - } -} - -void -GraphDelayCalc1::seedInvalidDelays() -{ - for (Vertex *vertex : *invalid_delays_) { - if (vertex->isRoot()) - seedRootSlew(vertex, arc_delay_calc_); - else { - if (search_non_latch_pred_->searchFrom(vertex)) - iter_->enqueue(vertex); - } - } - invalid_delays_->clear(); -} - -class FindNetDrvrs : public PinVisitor -{ -public: - FindNetDrvrs(PinSet &drvr_pins, - const Network *network, - const Graph *graph); - virtual void operator()(const Pin *pin); - -protected: - PinSet &drvr_pins_; - const Network *network_; - const Graph *graph_; -}; - -FindNetDrvrs::FindNetDrvrs(PinSet &drvr_pins, - const Network *network, - const Graph *graph) : - drvr_pins_(drvr_pins), - network_(network), - graph_(graph) -{ -} - -void -FindNetDrvrs::operator()(const Pin *pin) -{ - Vertex *vertex = graph_->pinDrvrVertex(pin); - if (isLeafDriver(pin, network_) - && !(vertex && vertex->isRoot())) - drvr_pins_.insert(pin); -} - -void -GraphDelayCalc1::ensureMultiDrvrNetsFound() -{ - if (!multi_drvr_nets_found_) { - LeafInstanceIterator *inst_iter = network_->leafInstanceIterator(); - while (inst_iter->hasNext()) { - Instance *inst = inst_iter->next(); - InstancePinIterator *pin_iter = network_->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - Vertex *drvr_vertex = graph_->pinDrvrVertex(pin); - if (network_->isDriver(pin) - && !multi_drvr_net_map_.hasKey(drvr_vertex)) { - PinSet drvr_pins(network_); - FindNetDrvrs visitor(drvr_pins, network_, graph_); - network_->visitConnectedPins(pin, visitor); - if (drvr_pins.size() > 1) - makeMultiDrvrNet(drvr_pins); - } - } - delete pin_iter; - } - delete inst_iter; - multi_drvr_nets_found_ = true; - } -} - -void -GraphDelayCalc1::makeMultiDrvrNet(PinSet &drvr_pins) -{ - debugPrint(debug_, "delay_calc", 3, "multi-driver net"); - VertexSet *drvr_vertices = new VertexSet(graph_); - MultiDrvrNet *multi_drvr = new MultiDrvrNet(drvr_vertices); - Level max_drvr_level = 0; - Vertex *max_drvr = nullptr; - PinSet::Iterator pin_iter(drvr_pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - Vertex *drvr_vertex = graph_->pinDrvrVertex(pin); - debugPrint(debug_, "delay_calc", 3, " %s", - network_->pathName(pin)); - multi_drvr_net_map_[drvr_vertex] = multi_drvr; - drvr_vertices->insert(drvr_vertex); - Level drvr_level = drvr_vertex->level(); - if (max_drvr == nullptr - || drvr_level > max_drvr_level) { - max_drvr = drvr_vertex; - max_drvr_level = drvr_level; - } - } - multi_drvr->setDcalcDrvr(max_drvr); - multi_drvr->findCaps(this, sdc_); -} - -static bool -isLeafDriver(const Pin *pin, - const Network *network) -{ - PortDirection *dir = network->direction(pin); - const Instance *inst = network->instance(pin); - return network->isLeaf(inst) && dir->isAnyOutput(); -} - -MultiDrvrNet * -GraphDelayCalc1::multiDrvrNet(const Vertex *drvr_vertex) const -{ - return multi_drvr_net_map_.findKey(drvr_vertex); -} - -void -GraphDelayCalc1::seedRootSlews() -{ - for (Vertex *vertex : *levelize_->roots()) - seedRootSlew(vertex, arc_delay_calc_); -} - -void -GraphDelayCalc1::seedRootSlew(Vertex *vertex, - ArcDelayCalc *arc_delay_calc) -{ - if (vertex->isDriver(network_)) - seedDrvrSlew(vertex, arc_delay_calc); - else - seedLoadSlew(vertex); - iter_->enqueueAdjacentVertices(vertex); -} - -void -GraphDelayCalc1::seedDrvrSlew(Vertex *drvr_vertex, - ArcDelayCalc *arc_delay_calc) -{ - const Pin *drvr_pin = drvr_vertex->pin(); - debugPrint(debug_, "delay_calc", 2, "seed driver slew %s", - drvr_vertex->name(sdc_network_)); - InputDrive *drive = 0; - if (network_->isTopLevelPort(drvr_pin)) { - Port *port = network_->port(drvr_pin); - drive = sdc_->findInputDrive(port); - } - for (auto rf : RiseFall::range()) { - for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { - if (drive) { - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); - const LibertyCell *drvr_cell; - const LibertyPort *from_port, *to_port; - float *from_slews; - drive->driveCell(rf, cnst_min_max, drvr_cell, from_port, - from_slews, to_port); - if (drvr_cell) { - if (from_port == nullptr) - from_port = driveCellDefaultFromPort(drvr_cell, to_port); - findInputDriverDelay(drvr_cell, drvr_pin, drvr_vertex, rf, - from_port, from_slews, to_port, dcalc_ap); - } - else - seedNoDrvrCellSlew(drvr_vertex, drvr_pin, rf, drive, dcalc_ap, - arc_delay_calc); - } - else - seedNoDrvrSlew(drvr_vertex, drvr_pin, rf, dcalc_ap, arc_delay_calc); - } - } -} - -void -GraphDelayCalc1::seedNoDrvrCellSlew(Vertex *drvr_vertex, - const Pin *drvr_pin, - const RiseFall *rf, - InputDrive *drive, - DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc) -{ - DcalcAPIndex ap_index = dcalc_ap->index(); - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); - Slew slew = default_slew; - float drive_slew; - bool exists; - drive->slew(rf, cnst_min_max, drive_slew, exists); - if (exists) - slew = drive_slew; - else { - // Top level bidirect driver uses load slew unless - // bidirect instance paths are disabled. - if (sdc_->bidirectDrvrSlewFromLoad(drvr_pin)) { - Vertex *load_vertex = graph_->pinLoadVertex(drvr_pin); - slew = graph_->slew(load_vertex, rf, ap_index); - } - } - Delay drive_delay = delay_zero; - float drive_res; - drive->driveResistance(rf, cnst_min_max, drive_res, exists); - Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, dcalc_ap); - if (exists) { - float cap = loadCap(drvr_pin, parasitic, rf, dcalc_ap); - drive_delay = cap * drive_res; - slew = cap * drive_res; - } - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) - graph_->setSlew(drvr_vertex, rf, ap_index, slew); - arc_delay_calc->inputPortDelay(drvr_pin, delayAsFloat(slew), rf, - parasitic, dcalc_ap); - annotateLoadDelays(drvr_vertex, rf, drive_delay, false, dcalc_ap, - arc_delay_calc); - arc_delay_calc->finishDrvrPin(); -} - -void -GraphDelayCalc1::seedNoDrvrSlew(Vertex *drvr_vertex, - const Pin *drvr_pin, - const RiseFall *rf, - DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc) -{ - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - DcalcAPIndex ap_index = dcalc_ap->index(); - Slew slew(default_slew); - // Top level bidirect driver uses load slew unless - // bidirect instance paths are disabled. - if (sdc_->bidirectDrvrSlewFromLoad(drvr_pin)) { - Vertex *load_vertex = graph_->pinLoadVertex(drvr_pin); - slew = graph_->slew(load_vertex, rf, ap_index); - } - if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) - graph_->setSlew(drvr_vertex, rf, ap_index, slew); - Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, dcalc_ap); - arc_delay_calc->inputPortDelay(drvr_pin, delayAsFloat(slew), rf, - parasitic, dcalc_ap); - annotateLoadDelays(drvr_vertex, rf, delay_zero, false, dcalc_ap, - arc_delay_calc); - arc_delay_calc->finishDrvrPin(); -} - -void -GraphDelayCalc1::seedLoadSlew(Vertex *vertex) -{ - const Pin *pin = vertex->pin(); - debugPrint(debug_, "delay_calc", 2, "seed load slew %s", - vertex->name(sdc_network_)); - ClockSet *clks = sdc_->findLeafPinClocks(pin); - initSlew(vertex); - for (auto rf : RiseFall::range()) { - for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (!vertex->slewAnnotated(rf, slew_min_max)) { - float slew = 0.0; - if (clks) { - slew = slew_min_max->initValue(); - ClockSet::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); - float clk_slew = clk->slew(rf, slew_min_max); - if (slew_min_max->compare(clk_slew, slew)) - slew = clk_slew; - } - } - DcalcAPIndex ap_index = dcalc_ap->index(); - graph_->setSlew(vertex, rf, ap_index, slew); - } - } - } -} - -// If a driving cell does not specify a -from_pin, the first port -// defined in the cell that has a timing group to the output port -// is used. Not exactly reasonable, but it's compatible. -LibertyPort * -GraphDelayCalc1::driveCellDefaultFromPort(const LibertyCell *cell, - const LibertyPort *to_port) -{ - LibertyPort *from_port = 0; - int from_port_index = 0; - for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, to_port)) { - LibertyPort *set_from_port = arc_set->from(); - int set_from_port_index = findPortIndex(cell, set_from_port); - if (from_port == nullptr - || set_from_port_index < from_port_index) { - from_port = set_from_port; - from_port_index = set_from_port_index; - } - } - return from_port; -} - -// Find the index that port is defined in cell. -int -GraphDelayCalc1::findPortIndex(const LibertyCell *cell, - const LibertyPort *port) -{ - int index = 0; - LibertyCellPortIterator port_iter(cell); - while (port_iter.hasNext()) { - LibertyPort *cell_port = port_iter.next(); - if (cell_port == port) - return index; - index++; - } - report_->critical(207, "port not found in cell"); - return 0; -} - -void -GraphDelayCalc1::findInputDriverDelay(const LibertyCell *drvr_cell, - const Pin *drvr_pin, - Vertex *drvr_vertex, - const RiseFall *rf, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port, - const DcalcAnalysisPt *dcalc_ap) -{ - debugPrint(debug_, "delay_calc", 2, " driver cell %s %s", - drvr_cell->name(), - rf->asString()); - for (TimingArcSet *arc_set : drvr_cell->timingArcSets(from_port, to_port)) { - for (TimingArc *arc : arc_set->arcs()) { - if (arc->toEdge()->asRiseFall() == rf) { - float from_slew = from_slews[arc->fromEdge()->index()]; - findInputArcDelay(drvr_cell, drvr_pin, drvr_vertex, - arc, from_slew, dcalc_ap); - } - } - } -} - -// Driving cell delay is the load dependent delay, which is the gate -// delay minus the intrinsic delay. Driving cell delays are annotated -// to the wire arcs from the input port pin to the load pins. -void -GraphDelayCalc1::findInputArcDelay(const LibertyCell *drvr_cell, - const Pin *drvr_pin, - Vertex *drvr_vertex, - const TimingArc *arc, - float from_slew, - const DcalcAnalysisPt *dcalc_ap) -{ - debugPrint(debug_, "delay_calc", 3, " %s %s -> %s %s (%s)", - arc->from()->name(), - arc->fromEdge()->asString(), - arc->to()->name(), - arc->toEdge()->asString(), - arc->role()->asString()); - RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); - if (drvr_rf) { - DcalcAPIndex ap_index = dcalc_ap->index(); - const Pvt *pvt = dcalc_ap->operatingConditions(); - Parasitic *drvr_parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, - dcalc_ap); - float load_cap = loadCap(drvr_pin, drvr_parasitic, drvr_rf, dcalc_ap); - - ArcDelay intrinsic_delay; - Slew intrinsic_slew; - arc_delay_calc_->gateDelay(drvr_cell, arc, Slew(from_slew), - 0.0, 0, 0.0, pvt, dcalc_ap, - intrinsic_delay, intrinsic_slew); - - // For input drivers there is no instance to find a related_output_pin. - ArcDelay gate_delay; - Slew gate_slew; - arc_delay_calc_->gateDelay(drvr_cell, arc, - Slew(from_slew), load_cap, - drvr_parasitic, 0.0, pvt, dcalc_ap, - gate_delay, gate_slew); - ArcDelay load_delay = gate_delay - intrinsic_delay; - debugPrint(debug_, "delay_calc", 3, - " gate delay = %s intrinsic = %s slew = %s", - delayAsString(gate_delay, this), - delayAsString(intrinsic_delay, this), - delayAsString(gate_slew, this)); - graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); - annotateLoadDelays(drvr_vertex, drvr_rf, load_delay, false, dcalc_ap, - arc_delay_calc_); - } -} - -void -GraphDelayCalc1::findDelays(Vertex *drvr_vertex) -{ - findVertexDelay(drvr_vertex, arc_delay_calc_, true); -} - -void -GraphDelayCalc1::findVertexDelay(Vertex *vertex, - ArcDelayCalc *arc_delay_calc, - bool propagate) -{ - const Pin *pin = vertex->pin(); - debugPrint(debug_, "delay_calc", 2, "find delays %s (%s)", - vertex->name(sdc_network_), - network_->cellName(network_->instance(pin))); - if (vertex->isRoot()) { - seedRootSlew(vertex, arc_delay_calc); - if (propagate) - iter_->enqueueAdjacentVertices(vertex); - } - else { - if (network_->isLeaf(pin)) { - if (vertex->isDriver(network_)) { - bool delay_changed = findDriverDelays(vertex, arc_delay_calc); - if (propagate) { - if (network_->direction(pin)->isInternal()) - enqueueTimingChecksEdges(vertex); - // Enqueue adjacent vertices even if the delays did not - // change when non-incremental to stride past annotations. - if (delay_changed || !incremental_) - iter_->enqueueAdjacentVertices(vertex); - } - } - else { - // Load vertex. - enqueueTimingChecksEdges(vertex); - // Enqueue driver vertices from this input load. - if (propagate) - iter_->enqueueAdjacentVertices(vertex); - } - } - // Bidirect port drivers are enqueued by their load vertex in - // annotateLoadDelays. - else if (vertex->isBidirectDriver() - && network_->isTopLevelPort(pin)) - seedRootSlew(vertex, arc_delay_calc); - } -} - -void -GraphDelayCalc1::enqueueTimingChecksEdges(Vertex *vertex) -{ - if (vertex->hasChecks()) { - VertexInEdgeIterator edge_iter(vertex, graph_); - UniqueLock lock(invalid_edge_lock_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isTimingCheck()) - invalid_check_edges_.insert(edge); - } - } - if (vertex->isCheckClk()) { - VertexOutEdgeIterator edge_iter(vertex, graph_); - UniqueLock lock(invalid_edge_lock_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isTimingCheck()) - invalid_check_edges_.insert(edge); - } - } - if (network_->isLatchData(vertex->pin())) { - // Latch D->Q arcs have to be re-evaled if level(D) > level(E) - // because levelization does not traverse D->Q arcs to break loops. - VertexOutEdgeIterator edge_iter(vertex, graph_); - UniqueLock lock(invalid_edge_lock_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role() == TimingRole::latchDtoQ()) - invalid_latch_edges_.insert(edge); - } - } -} - -bool -GraphDelayCalc1::findDriverDelays(Vertex *drvr_vertex, - ArcDelayCalc *arc_delay_calc) -{ - bool delay_changed = false; - MultiDrvrNet *multi_drvr = multiDrvrNet(drvr_vertex); - if (multi_drvr) { - Vertex *dcalc_drvr = multi_drvr->dcalcDrvr(); - if (drvr_vertex == dcalc_drvr) { - bool init_load_slews = true; - for (Vertex *drvr_vertex : *multi_drvr->drvrs()) { - // Only init load slews once so previous driver dcalc results - // aren't clobbered. - delay_changed |= findDriverDelays1(drvr_vertex, init_load_slews, - multi_drvr, arc_delay_calc); - init_load_slews = false; - } - } - } - else - delay_changed = findDriverDelays1(drvr_vertex, true, nullptr, arc_delay_calc); - arc_delay_calc->finishDrvrPin(); - return delay_changed; -} - -bool -GraphDelayCalc1::findDriverDelays1(Vertex *drvr_vertex, - bool init_load_slews, - MultiDrvrNet *multi_drvr, - ArcDelayCalc *arc_delay_calc) -{ - const Pin *drvr_pin = drvr_vertex->pin(); - Instance *drvr_inst = network_->instance(drvr_pin); - LibertyCell *drvr_cell = network_->libertyCell(drvr_inst); - initSlew(drvr_vertex); - initWireDelays(drvr_vertex, init_load_slews); - bool delay_changed = false; - bool has_delays = false; - VertexInEdgeIterator edge_iter(drvr_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - // Don't let disabled edges set slews that influence downstream delays. - if (search_pred_->searchFrom(from_vertex) - && search_pred_->searchThru(edge) - && !edge->role()->isLatchDtoQ()) { - delay_changed |= findDriverEdgeDelays(drvr_cell, drvr_inst, drvr_pin, - drvr_vertex, multi_drvr, edge, - arc_delay_calc); - has_delays = true; - } - } - if (!has_delays) - zeroSlewAndWireDelays(drvr_vertex); - if (delay_changed && observer_) - observer_->delayChangedTo(drvr_vertex); - return delay_changed; -} - -// Init slews to zero on root vertices that are not inputs, such as -// floating input pins. -void -GraphDelayCalc1::initRootSlews(Vertex *vertex) -{ - for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - DcalcAPIndex ap_index = dcalc_ap->index(); - for (auto rf : RiseFall::range()) { - if (!vertex->slewAnnotated(rf, slew_min_max)) - graph_->setSlew(vertex, rf, ap_index, default_slew); - } - } -} - -void -GraphDelayCalc1::findLatchEdgeDelays(Edge *edge) -{ - Vertex *drvr_vertex = edge->to(graph_); - const Pin *drvr_pin = drvr_vertex->pin(); - Instance *drvr_inst = network_->instance(drvr_pin); - LibertyCell *drvr_cell = network_->libertyCell(drvr_inst); - debugPrint(debug_, "delay_calc", 2, "find latch D->Q %s", - sdc_network_->pathName(drvr_inst)); - bool delay_changed = findDriverEdgeDelays(drvr_cell, drvr_inst, drvr_pin, - drvr_vertex, nullptr, edge, arc_delay_calc_); - if (delay_changed && observer_) - observer_->delayChangedTo(drvr_vertex); -} - -bool -GraphDelayCalc1::findDriverEdgeDelays(LibertyCell *drvr_cell, - Instance *drvr_inst, - const Pin *drvr_pin, - Vertex *drvr_vertex, - MultiDrvrNet *multi_drvr, - Edge *edge, - ArcDelayCalc *arc_delay_calc) -{ - Vertex *in_vertex = edge->from(graph_); - TimingArcSet *arc_set = edge->timingArcSet(); - const LibertyPort *related_out_port = arc_set->relatedOut(); - const Pin *related_out_pin = 0; - bool delay_changed = false; - if (related_out_port) - related_out_pin = network_->findPin(drvr_inst, related_out_port); - for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { - const Pvt *pvt = sdc_->pvt(drvr_inst, dcalc_ap->constraintMinMax()); - if (pvt == nullptr) - pvt = dcalc_ap->operatingConditions(); - for (TimingArc *arc : arc_set->arcs()) { - const RiseFall *rf = arc->toEdge()->asRiseFall(); - Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, - dcalc_ap); - float related_out_cap = 0.0; - if (related_out_pin) { - Parasitic *related_out_parasitic = - arc_delay_calc->findParasitic(related_out_pin, rf, dcalc_ap); - related_out_cap = loadCap(related_out_pin, - related_out_parasitic, - rf, dcalc_ap); - } - delay_changed |= findArcDelay(drvr_cell, drvr_pin, drvr_vertex, - multi_drvr, arc, parasitic, - related_out_cap, - in_vertex, edge, pvt, dcalc_ap, - arc_delay_calc); - } - } - - if (delay_changed && observer_) { - observer_->delayChangedFrom(in_vertex); - observer_->delayChangedFrom(drvr_vertex); - } - return delay_changed; -} - -float -GraphDelayCalc1::loadCap(const Pin *drvr_pin, - const DcalcAnalysisPt *dcalc_ap) const -{ - const MinMax *min_max = dcalc_ap->constraintMinMax(); - float load_cap = 0.0; - for (auto drvr_rf : RiseFall::range()) { - Parasitic *drvr_parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, - dcalc_ap); - float cap = loadCap(drvr_pin, nullptr, drvr_parasitic, drvr_rf, dcalc_ap); - arc_delay_calc_->finishDrvrPin(); - if (min_max->compare(cap, load_cap)) - load_cap = cap; - } - return load_cap; -} - -float -GraphDelayCalc1::loadCap(const Pin *drvr_pin, - const RiseFall *drvr_rf, - const DcalcAnalysisPt *dcalc_ap) const -{ - Parasitic *drvr_parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, - dcalc_ap); - float cap = loadCap(drvr_pin, nullptr, drvr_parasitic, drvr_rf, dcalc_ap); - return cap; -} - -float -GraphDelayCalc1::loadCap(const Pin *drvr_pin, - const Parasitic *drvr_parasitic, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) const -{ - return loadCap(drvr_pin, nullptr, drvr_parasitic, rf, dcalc_ap); -} - -float -GraphDelayCalc1::loadCap(const Pin *drvr_pin, - MultiDrvrNet *multi_drvr, - const Parasitic *drvr_parasitic, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) const -{ - float pin_cap, wire_cap; - bool has_net_load; - float fanout; - if (multi_drvr) - multi_drvr->netCaps(rf, dcalc_ap, - pin_cap, wire_cap, fanout, has_net_load); - else - netCaps(drvr_pin, rf, dcalc_ap, - pin_cap, wire_cap, fanout, has_net_load); - loadCap(drvr_parasitic, has_net_load, pin_cap, wire_cap); - return wire_cap + pin_cap; -} - -void -GraphDelayCalc1::loadCap(const Pin *drvr_pin, - const Parasitic *drvr_parasitic, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - float &pin_cap, - float &wire_cap) const -{ - bool has_net_load; - float fanout; - // Find pin and external pin/wire capacitance. - netCaps(drvr_pin, rf, dcalc_ap, - pin_cap, wire_cap, fanout, has_net_load); - loadCap(drvr_parasitic, has_net_load, pin_cap, wire_cap); -} - -void -GraphDelayCalc1::loadCap(const Parasitic *drvr_parasitic, - bool has_net_load, - // Return values. - float &pin_cap, - float &wire_cap) const -{ - // set_load net has precidence over parasitics. - if (!has_net_load && drvr_parasitic) { - if (parasitics_->isParasiticNetwork(drvr_parasitic)) - wire_cap += parasitics_->capacitance(drvr_parasitic); - else { - // PiModel includes both pin and external caps. - float cap = parasitics_->capacitance(drvr_parasitic); - if (pin_cap > cap) { - pin_cap = 0.0; - wire_cap = cap; - } - else - wire_cap = cap - pin_cap; - } - } -} - -void -GraphDelayCalc1::netCaps(const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_net_load) const -{ - MultiDrvrNet *multi_drvr = nullptr; - if (graph_) { - Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin); - multi_drvr = multiDrvrNet(drvr_vertex); - } - if (multi_drvr) - multi_drvr->netCaps(rf, dcalc_ap, - pin_cap, wire_cap, fanout, has_net_load); - else { - const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); - const Corner *corner = dcalc_ap->corner(); - const MinMax *min_max = dcalc_ap->constraintMinMax(); - // Find pin and external pin/wire capacitance. - sdc_->connectedCap(drvr_pin, rf, op_cond, corner, min_max, - pin_cap, wire_cap, fanout, has_net_load); - } -} - -void -GraphDelayCalc1::initSlew(Vertex *vertex) -{ - for (auto rf : RiseFall::range()) { - for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (!vertex->slewAnnotated(rf, slew_min_max)) { - DcalcAPIndex ap_index = dcalc_ap->index(); - graph_->setSlew(vertex, rf, ap_index, slew_min_max->initValue()); - } - } - } -} - -void -GraphDelayCalc1::zeroSlewAndWireDelays(Vertex *drvr_vertex) -{ - for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - for (auto rf : RiseFall::range()) { - // Init drvr slew. - if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) { - DcalcAPIndex ap_index = dcalc_ap->index(); - graph_->setSlew(drvr_vertex, rf, ap_index, slew_min_max->initValue()); - } - - // Init wire delays and slews. - VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *wire_edge = edge_iter.next(); - if (wire_edge->isWire()) { - Vertex *load_vertex = wire_edge->to(graph_); - if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) - graph_->setWireArcDelay(wire_edge, rf, ap_index, 0.0); - // Init load vertex slew. - if (!load_vertex->slewAnnotated(rf, slew_min_max)) - graph_->setSlew(load_vertex, rf, ap_index, 0.0); - } - } - } - } -} - -// Init wire delays and load slews. -void -GraphDelayCalc1::initWireDelays(Vertex *drvr_vertex, - bool init_load_slews) -{ - VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *wire_edge = edge_iter.next(); - if (wire_edge->isWire()) { - Vertex *load_vertex = wire_edge->to(graph_); - for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *delay_min_max = dcalc_ap->delayMinMax(); - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - Delay delay_init_value(delay_min_max->initValue()); - Slew slew_init_value(slew_min_max->initValue()); - DcalcAPIndex ap_index = dcalc_ap->index(); - for (auto rf : RiseFall::range()) { - if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) - graph_->setWireArcDelay(wire_edge, rf, ap_index, delay_init_value); - // Init load vertex slew. - if (init_load_slews - && !load_vertex->slewAnnotated(rf, slew_min_max)) - graph_->setSlew(load_vertex, rf, ap_index, slew_init_value); - } - } - } - } -} - -// Call the arc delay calculator to find the delay thru a single gate -// input to output timing arc, the wire delays from the gate output to -// each load pin, and the slew at each load pin. Annotate the graph -// with the results. -bool -GraphDelayCalc1::findArcDelay(LibertyCell *drvr_cell, - const Pin *drvr_pin, - Vertex *drvr_vertex, - MultiDrvrNet *multi_drvr, - TimingArc *arc, - Parasitic *drvr_parasitic, - float related_out_cap, - Vertex *from_vertex, - Edge *edge, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc) -{ - bool delay_changed = false; - RiseFall *from_rf = arc->fromEdge()->asRiseFall(); - RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); - if (from_rf && drvr_rf) { - DcalcAPIndex ap_index = dcalc_ap->index(); - debugPrint(debug_, "delay_calc", 3, - " %s %s -> %s %s (%s) corner:%s/%s", - arc->from()->name(), - arc->fromEdge()->asString(), - arc->to()->name(), - arc->toEdge()->asString(), - arc->role()->asString(), - dcalc_ap->corner()->name(), - dcalc_ap->delayMinMax()->asString()); - // Delay calculation is done even when the gate delays/slews are - // annotated because the wire delays may not be annotated. - const Slew from_slew = edgeFromSlew(from_vertex, from_rf, edge, dcalc_ap); - ArcDelay gate_delay; - Slew gate_slew; - if (multi_drvr - && arc->to()->direction()->isOutput()) - parallelGateDelay(multi_drvr, drvr_cell, drvr_pin, arc, - pvt, dcalc_ap, from_slew, drvr_parasitic, - related_out_cap, - arc_delay_calc, - gate_delay, gate_slew); - else { - float load_cap = loadCap(drvr_pin, multi_drvr, drvr_parasitic, - drvr_rf, dcalc_ap); - arc_delay_calc->gateDelay(drvr_cell, arc, - from_slew, load_cap, drvr_parasitic, - related_out_cap, pvt, dcalc_ap, - gate_delay, gate_slew); - } - debugPrint(debug_, "delay_calc", 3, - " gate delay = %s slew = %s", - delayAsString(gate_delay, this), - delayAsString(gate_slew, this)); - // Merge slews. - const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (delayGreater(gate_slew, drvr_slew, dcalc_ap->slewMinMax(), this) - && !drvr_vertex->slewAnnotated(drvr_rf, slew_min_max) - && !edge->role()->isLatchDtoQ()) - graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); - if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { - const ArcDelay &prev_gate_delay = graph_->arcDelay(edge,arc,ap_index); - float gate_delay1 = delayAsFloat(gate_delay); - float prev_gate_delay1 = delayAsFloat(prev_gate_delay); - if (prev_gate_delay1 == 0.0 - || (abs(gate_delay1 - prev_gate_delay1) / prev_gate_delay1 - > incremental_delay_tolerance_)) - delay_changed = true; - graph_->setArcDelay(edge, arc, ap_index, gate_delay); - } - if (!edge->role()->isLatchDtoQ()) - annotateLoadDelays(drvr_vertex, drvr_rf, delay_zero, true, dcalc_ap, - arc_delay_calc); - } - return delay_changed; -} - -void -GraphDelayCalc1::parallelGateDelay(MultiDrvrNet *multi_drvr, - LibertyCell *drvr_cell, - const Pin *drvr_pin, - TimingArc *arc, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - const Slew from_slew, - Parasitic *drvr_parasitic, - float related_out_cap, - ArcDelayCalc *arc_delay_calc, - // Return values. - ArcDelay &gate_delay, - Slew &gate_slew) -{ - ArcDelay intrinsic_delay; - Slew intrinsic_slew; - arc_delay_calc->gateDelay(drvr_cell, arc, from_slew, - 0.0, 0, 0.0, pvt, dcalc_ap, - intrinsic_delay, intrinsic_slew); - ArcDelay parallel_delay; - Slew parallel_slew; - const RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); - multi_drvr->parallelDelaySlew(drvr_rf, dcalc_ap, arc_delay_calc, this, - parallel_delay, parallel_slew); - - gate_delay = parallel_delay + intrinsic_delay; - gate_slew = parallel_slew; - - float load_cap = loadCap(drvr_pin, multi_drvr, drvr_parasitic, - drvr_rf, dcalc_ap); - Delay gate_delay1; - Slew gate_slew1; - arc_delay_calc->gateDelay(drvr_cell, arc, - from_slew, load_cap, drvr_parasitic, - related_out_cap, pvt, dcalc_ap, - gate_delay1, gate_slew1); - float factor = delayRatio(gate_slew, gate_slew1); - arc_delay_calc->setMultiDrvrSlewFactor(factor); -} - -void -GraphDelayCalc1::findMultiDrvrGateDelay(MultiDrvrNet *multi_drvr, - const RiseFall *drvr_rf, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc, - // Return values. - ArcDelay ¶llel_delay, - Slew ¶llel_slew) -{ - ArcDelay delay_sum = 1.0; - Slew slew_sum = 1.0; - for (Vertex *drvr_vertex1 : *multi_drvr->drvrs()) { - Pin *drvr_pin1 = drvr_vertex1->pin(); - Instance *drvr_inst1 = network_->instance(drvr_pin1); - LibertyCell *drvr_cell1 = network_->libertyCell(drvr_inst1); - if (network_->isDriver(drvr_pin1)) { - VertexInEdgeIterator edge_iter(drvr_vertex1, graph_); - while (edge_iter.hasNext()) { - Edge *edge1 = edge_iter.next(); - TimingArcSet *arc_set1 = edge1->timingArcSet(); - const LibertyPort *related_out_port = arc_set1->relatedOut(); - for (TimingArc *arc1 : arc_set1->arcs()) { - RiseFall *drvr_rf1 = arc1->toEdge()->asRiseFall(); - if (drvr_rf1 == drvr_rf) { - Vertex *from_vertex1 = edge1->from(graph_); - RiseFall *from_rf1 = arc1->fromEdge()->asRiseFall(); - Slew from_slew1 = edgeFromSlew(from_vertex1, from_rf1, edge1, dcalc_ap); - ArcDelay intrinsic_delay1; - Slew intrinsic_slew1; - arc_delay_calc->gateDelay(drvr_cell1, arc1, from_slew1, - 0.0, 0, 0.0, pvt, dcalc_ap, - intrinsic_delay1, intrinsic_slew1); - Parasitic *parasitic1 = - arc_delay_calc->findParasitic(drvr_pin1, drvr_rf1, dcalc_ap); - const Pin *related_out_pin1 = 0; - float related_out_cap1 = 0.0; - if (related_out_port) { - Instance *inst1 = network_->instance(drvr_pin1); - related_out_pin1 = network_->findPin(inst1, related_out_port); - if (related_out_pin1) { - Parasitic *related_out_parasitic1 = - arc_delay_calc->findParasitic(related_out_pin1, drvr_rf, - dcalc_ap); - related_out_cap1 = loadCap(related_out_pin1, - related_out_parasitic1, - drvr_rf, dcalc_ap); - } - } - float load_cap1 = loadCap(drvr_pin1, parasitic1, - drvr_rf, dcalc_ap); - ArcDelay gate_delay1; - Slew gate_slew1; - arc_delay_calc->gateDelay(drvr_cell1, arc1, - from_slew1, load_cap1, parasitic1, - related_out_cap1, pvt, dcalc_ap, - gate_delay1, gate_slew1); - delay_sum += 1.0F / (gate_delay1 - intrinsic_delay1); - slew_sum += 1.0F / gate_slew1; - } - } - } - } - } - parallel_delay = 1.0F / delay_sum; - parallel_slew = 1.0F / slew_sum; -} - -// Use clock slew for register/latch clk->q edges. -Slew -GraphDelayCalc1::edgeFromSlew(const Vertex *from_vertex, - const RiseFall *from_rf, - const Edge *edge, - const DcalcAnalysisPt *dcalc_ap) -{ - const TimingRole *role = edge->role(); - if (role->genericRole() == TimingRole::regClkToQ() - && clk_network_->isIdealClock(from_vertex->pin())) - return clk_network_->idealClkSlew(from_vertex->pin(), from_rf, - dcalc_ap->slewMinMax()); - else - return graph_->slew(from_vertex, from_rf, dcalc_ap->index()); -} - -// Annotate wire arc delays and load pin slews. -// extra_delay is additional wire delay to add to delay returned -// by the delay calculator. -void -GraphDelayCalc1::annotateLoadDelays(Vertex *drvr_vertex, - const RiseFall *drvr_rf, - const ArcDelay &extra_delay, - bool merge, - const DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc) -{ - DcalcAPIndex ap_index = dcalc_ap->index(); - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *wire_edge = edge_iter.next(); - if (wire_edge->isWire()) { - Vertex *load_vertex = wire_edge->to(graph_); - Pin *load_pin = load_vertex->pin(); - ArcDelay wire_delay; - Slew load_slew; - arc_delay_calc->loadDelay(load_pin, wire_delay, load_slew); - debugPrint(debug_, "delay_calc", 3, - " %s load delay = %s slew = %s", - load_vertex->name(sdc_network_), - delayAsString(wire_delay, this), - delayAsString(load_slew, this)); - if (!load_vertex->slewAnnotated(drvr_rf, slew_min_max)) { - if (drvr_vertex->slewAnnotated(drvr_rf, slew_min_max)) { - // Copy the driver slew to the load if it is annotated. - const Slew &drvr_slew = graph_->slew(drvr_vertex,drvr_rf,ap_index); - graph_->setSlew(load_vertex, drvr_rf, ap_index, drvr_slew); - } - else { - const Slew &slew = graph_->slew(load_vertex, drvr_rf, ap_index); - if (!merge - || delayGreater(load_slew, slew, slew_min_max, this)) - graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew); - } - } - if (!graph_->wireDelayAnnotated(wire_edge, drvr_rf, ap_index)) { - // Multiple timing arcs with the same output transition - // annotate the same wire edges so they must be combined - // rather than set. - const ArcDelay &delay = graph_->wireArcDelay(wire_edge, drvr_rf, - ap_index); - Delay wire_delay_extra = extra_delay + wire_delay; - const MinMax *delay_min_max = dcalc_ap->delayMinMax(); - if (!merge - || delayGreater(wire_delay_extra, delay, delay_min_max, this)) { - graph_->setWireArcDelay(wire_edge, drvr_rf, ap_index, - wire_delay_extra); - if (observer_) - observer_->delayChangedTo(load_vertex); - } - } - // Enqueue bidirect driver from load vertex. - if (sdc_->bidirectDrvrSlewFromLoad(load_pin)) - iter_->enqueue(graph_->pinDrvrVertex(load_pin)); - } - } -} - -void -GraphDelayCalc1::findCheckEdgeDelays(Edge *edge, - ArcDelayCalc *arc_delay_calc) -{ - Vertex *from_vertex = edge->from(graph_); - Vertex *to_vertex = edge->to(graph_); - TimingArcSet *arc_set = edge->timingArcSet(); - const Pin *to_pin = to_vertex->pin(); - Instance *inst = network_->instance(to_pin); - const LibertyCell *cell = network_->libertyCell(inst); - debugPrint(debug_, "delay_calc", 2, "find check %s %s -> %s", - sdc_network_->pathName(inst), - network_->portName(from_vertex->pin()), - network_->portName(to_pin)); - bool delay_changed = false; - for (TimingArc *arc : arc_set->arcs()) { - RiseFall *from_rf = arc->fromEdge()->asRiseFall(); - RiseFall *to_rf = arc->toEdge()->asRiseFall(); - if (from_rf && to_rf) { - const LibertyPort *related_out_port = arc_set->relatedOut(); - const Pin *related_out_pin = 0; - if (related_out_port) - related_out_pin = network_->findPin(inst, related_out_port); - for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); - if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { - const Pvt *pvt = sdc_->pvt(inst,dcalc_ap->constraintMinMax()); - if (pvt == nullptr) - pvt = dcalc_ap->operatingConditions(); - const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, - dcalc_ap); - int slew_index = dcalc_ap->checkDataSlewIndex(); - const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index); - debugPrint(debug_, "delay_calc", 3, - " %s %s -> %s %s (%s)", - arc_set->from()->name(), - arc->fromEdge()->asString(), - arc_set->to()->name(), - arc->toEdge()->asString(), - arc_set->role()->asString()); - debugPrint(debug_, "delay_calc", 3, - " from_slew = %s to_slew = %s", - delayAsString(from_slew, this), - delayAsString(to_slew, this)); - float related_out_cap = 0.0; - if (related_out_pin) { - Parasitic *related_out_parasitic = - arc_delay_calc->findParasitic(related_out_pin, to_rf, dcalc_ap); - related_out_cap = loadCap(related_out_pin, - related_out_parasitic, - to_rf, dcalc_ap); - } - ArcDelay check_delay; - arc_delay_calc->checkDelay(cell, arc, - from_slew, to_slew, - related_out_cap, - pvt, dcalc_ap, - check_delay); - debugPrint(debug_, "delay_calc", 3, - " check_delay = %s", - delayAsString(check_delay, this)); - graph_->setArcDelay(edge, arc, ap_index, check_delay); - delay_changed = true; - } - } - } - } - - if (delay_changed && observer_) - observer_->checkDelayChangedTo(to_vertex); -} - -// Use clock slew for timing check clock edges. -Slew -GraphDelayCalc1::checkEdgeClkSlew(const Vertex *from_vertex, - const RiseFall *from_rf, - const DcalcAnalysisPt *dcalc_ap) -{ - if (clk_network_->isIdealClock(from_vertex->pin())) - return clk_network_->idealClkSlew(from_vertex->pin(), from_rf, - dcalc_ap->checkClkSlewMinMax()); - else - return graph_->slew(from_vertex, from_rf, dcalc_ap->checkClkSlewIndex()); -} - -//////////////////////////////////////////////////////////////// - -float -GraphDelayCalc1::ceff(Edge *edge, - TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap) -{ - Vertex *from_vertex = edge->from(graph_); - Vertex *to_vertex = edge->to(graph_); - Pin *to_pin = to_vertex->pin(); - Instance *inst = network_->instance(to_pin); - LibertyCell *cell = network_->libertyCell(inst); - TimingArcSet *arc_set = edge->timingArcSet(); - float ceff = 0.0; - const Pvt *pvt = sdc_->pvt(inst, dcalc_ap->constraintMinMax()); - if (pvt == nullptr) - pvt = dcalc_ap->operatingConditions(); - RiseFall *from_rf = arc->fromEdge()->asRiseFall(); - RiseFall *to_rf = arc->toEdge()->asRiseFall(); - if (from_rf && to_rf) { - const LibertyPort *related_out_port = arc_set->relatedOut(); - const Pin *related_out_pin = 0; - if (related_out_port) - related_out_pin = network_->findPin(inst, related_out_port); - float related_out_cap = 0.0; - if (related_out_pin) { - Parasitic *related_out_parasitic = - arc_delay_calc_->findParasitic(related_out_pin, to_rf, dcalc_ap); - related_out_cap = loadCap(related_out_pin, related_out_parasitic, - to_rf, dcalc_ap); - } - Parasitic *to_parasitic = arc_delay_calc_->findParasitic(to_pin, to_rf, - dcalc_ap); - const Slew &from_slew = edgeFromSlew(from_vertex, from_rf, edge, dcalc_ap); - float load_cap = loadCap(to_pin, to_parasitic, to_rf, dcalc_ap); - ceff = arc_delay_calc_->ceff(cell, arc, - from_slew, load_cap, to_parasitic, - related_out_cap, pvt, dcalc_ap); - arc_delay_calc_->finishDrvrPin(); - } - return ceff; -} - -//////////////////////////////////////////////////////////////// - -string -GraphDelayCalc1::reportDelayCalc(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMax *min_max, - int digits) -{ - Vertex *from_vertex = edge->from(graph_); - Vertex *to_vertex = edge->to(graph_); - Pin *to_pin = to_vertex->pin(); - TimingRole *role = arc->role(); - Instance *inst = network_->instance(to_pin); - LibertyCell *cell = network_->libertyCell(inst); - TimingArcSet *arc_set = edge->timingArcSet(); - string result; - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - const Pvt *pvt = sdc_->pvt(inst, dcalc_ap->constraintMinMax()); - if (pvt == nullptr) - pvt = dcalc_ap->operatingConditions(); - RiseFall *from_rf = arc->fromEdge()->asRiseFall(); - RiseFall *to_rf = arc->toEdge()->asRiseFall(); - if (from_rf && to_rf) { - const LibertyPort *related_out_port = arc_set->relatedOut(); - const Pin *related_out_pin = 0; - if (related_out_port) - related_out_pin = network_->findPin(inst, related_out_port); - float related_out_cap = 0.0; - if (related_out_pin) { - Parasitic *related_out_parasitic = - arc_delay_calc_->findParasitic(related_out_pin, to_rf, dcalc_ap); - related_out_cap = loadCap(related_out_pin, related_out_parasitic, - to_rf, dcalc_ap); - } - if (role->isTimingCheck()) { - const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, dcalc_ap); - int slew_index = dcalc_ap->checkDataSlewIndex(); - const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index); - bool from_ideal_clk = clk_network_->isIdealClock(from_vertex->pin()); - const char *from_slew_annotation = from_ideal_clk ? " (ideal clock)" : nullptr; - result = arc_delay_calc_->reportCheckDelay(cell, arc, from_slew, - from_slew_annotation, to_slew, - related_out_cap, pvt, dcalc_ap, - digits); - } - else { - Parasitic *to_parasitic = - arc_delay_calc_->findParasitic(to_pin, to_rf, dcalc_ap); - const Slew &from_slew = edgeFromSlew(from_vertex, from_rf, edge, dcalc_ap); - float load_cap = loadCap(to_pin, to_parasitic, to_rf, dcalc_ap); - result = arc_delay_calc_->reportGateDelay(cell, arc, - from_slew, load_cap, to_parasitic, - related_out_cap, pvt, dcalc_ap, digits); - } - arc_delay_calc_->finishDrvrPin(); - } - return result; -} - -} // namespace diff --git a/dcalc/GraphDelayCalc1.hh b/dcalc/GraphDelayCalc1.hh deleted file mode 100644 index 84189dcb..00000000 --- a/dcalc/GraphDelayCalc1.hh +++ /dev/null @@ -1,236 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2023, Parallax Software, Inc. -// -// 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 . - -#pragma once - -#include - -#include "Delay.hh" -#include "GraphDelayCalc.hh" - -namespace sta { - -class MultiDrvrNet; -class FindVertexDelays; -class Corner; - -typedef Map MultiDrvrNetMap; - -// This class traverses the graph calling the arc delay calculator and -// annotating delays on graph edges. -class GraphDelayCalc1 : public GraphDelayCalc -{ -public: - GraphDelayCalc1(StaState *sta); - virtual ~GraphDelayCalc1(); - virtual void copyState(const StaState *sta); - virtual void delaysInvalid(); - virtual void delayInvalid(Vertex *vertex); - virtual void delayInvalid(const Pin *pin); - virtual void deleteVertexBefore(Vertex *vertex); - virtual void clear(); - virtual void findDelays(Level level); - virtual void findDelays(Vertex *drvr_vertex); - virtual string reportDelayCalc(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMax *min_max, - int digits); - virtual float incrementalDelayTolerance(); - virtual void setIncrementalDelayTolerance(float tol); - virtual void setObserver(DelayCalcObserver *observer); - // Load pin_cap + wire_cap. - virtual float loadCap(const Pin *drvr_pin, - const RiseFall *drvr_rf, - const DcalcAnalysisPt *dcalc_ap) const; - virtual float loadCap(const Pin *drvr_pin, - const DcalcAnalysisPt *dcalc_ap) const; - virtual void loadCap(const Pin *drvr_pin, - const Parasitic *drvr_parasitic, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - float &pin_cap, - float &wire_cap) const; - virtual float loadCap(const Pin *drvr_pin, - const Parasitic *drvr_parasitic, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) const; - virtual void netCaps(const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_set_load) const; - float ceff(Edge *edge, - TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap); - -protected: - void seedInvalidDelays(); - void ensureMultiDrvrNetsFound(); - void makeMultiDrvrNet(PinSet &drvr_pins); - void initSlew(Vertex *vertex); - void seedRootSlew(Vertex *vertex, - ArcDelayCalc *arc_delay_calc); - void seedRootSlews(); - void seedDrvrSlew(Vertex *vertex, - ArcDelayCalc *arc_delay_calc); - void seedNoDrvrSlew(Vertex *drvr_vertex, - const Pin *drvr_pin, - const RiseFall *rf, - DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc); - void seedNoDrvrCellSlew(Vertex *drvr_vertex, - const Pin *drvr_pin, - const RiseFall *rf, - InputDrive *drive, - DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc); - void seedLoadSlew(Vertex *vertex); - void setInputPortWireDelays(Vertex *vertex); - void findInputDriverDelay(const LibertyCell *drvr_cell, - const Pin *drvr_pin, - Vertex *drvr_vertex, - const RiseFall *rf, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port, - const DcalcAnalysisPt *dcalc_ap); - LibertyPort *driveCellDefaultFromPort(const LibertyCell *cell, - const LibertyPort *to_port); - int findPortIndex(const LibertyCell *cell, - const LibertyPort *port); - void findInputArcDelay(const LibertyCell *drvr_cell, - const Pin *drvr_pin, - Vertex *drvr_vertex, - const TimingArc *arc, - float from_slew, - const DcalcAnalysisPt *dcalc_ap); - bool findDriverDelays(Vertex *drvr_vertex, - ArcDelayCalc *arc_delay_calc); - bool findDriverDelays1(Vertex *drvr_vertex, - bool init_load_slews, - MultiDrvrNet *multi_drvr, - ArcDelayCalc *arc_delay_calc); - bool findDriverEdgeDelays(LibertyCell *drvr_cell, - Instance *drvr_inst, - const Pin *drvr_pin, - Vertex *drvr_vertex, - MultiDrvrNet *multi_drvr, - Edge *edge, - ArcDelayCalc *arc_delay_calc); - void initWireDelays(Vertex *drvr_vertex, - bool init_load_slews); - void initRootSlews(Vertex *vertex); - void zeroSlewAndWireDelays(Vertex *drvr_vertex); - void findVertexDelay(Vertex *vertex, - ArcDelayCalc *arc_delay_calc, - bool propagate); - void enqueueTimingChecksEdges(Vertex *vertex); - bool findArcDelay(LibertyCell *drvr_cell, - const Pin *drvr_pin, - Vertex *drvr_vertex, - MultiDrvrNet *multi_drvr, - TimingArc *arc, - Parasitic *drvr_parasitic, - float related_out_cap, - Vertex *from_vertex, - Edge *edge, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc); - void annotateLoadDelays(Vertex *drvr_vertex, - const RiseFall *drvr_rf, - const ArcDelay &extra_delay, - bool merge, - const DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc); - void findLatchEdgeDelays(Edge *edge); - void findCheckEdgeDelays(Edge *edge, - ArcDelayCalc *arc_delay_calc); - void findMultiDrvrGateDelay(MultiDrvrNet *multi_drvr, - const RiseFall *drvr_rf, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - ArcDelayCalc *arc_delay_calc, - // Return values. - ArcDelay ¶llel_delay, - Slew ¶llel_slew); - void parallelGateDelay(MultiDrvrNet *multi_drvr, - LibertyCell *drvr_cell, - const Pin *drvr_pin, - TimingArc *arc, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - const Slew from_slew, - Parasitic *drvr_parasitic, - float related_out_cap, - ArcDelayCalc *arc_delay_calc, - // Return values. - ArcDelay &gate_delay, - Slew &gate_slew); - void deleteMultiDrvrNets(); - Slew edgeFromSlew(const Vertex *from_vertex, - const RiseFall *from_rf, - const Edge *edge, - const DcalcAnalysisPt *dcalc_ap); - Slew checkEdgeClkSlew(const Vertex *from_vertex, - const RiseFall *from_rf, - const DcalcAnalysisPt *dcalc_ap); - bool bidirectDrvrSlewFromLoad(const Vertex *vertex) const; - MultiDrvrNet *multiDrvrNet(const Vertex *drvr_vertex) const; - void loadCap(const Parasitic *drvr_parasitic, - bool has_set_load, - // Return values. - float &pin_cap, - float &wire_cap) const; - float loadCap(const Pin *drvr_pin, - MultiDrvrNet *multi_drvr, - const Parasitic *drvr_parasitic, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) const; - - // Observer for edge delay changes. - DelayCalcObserver *observer_; - bool delays_seeded_; - bool incremental_; - bool delays_exist_; - // Vertices with invalid -to delays. - VertexSet *invalid_delays_; - // Timing check edges with invalid delays. - EdgeSet invalid_check_edges_; - // Latch D->Q edges with invalid delays. - EdgeSet invalid_latch_edges_; - // shared by invalid_check_edges_ and invalid_latch_edges_ - std::mutex invalid_edge_lock_; - SearchPred *search_pred_; - SearchPred *search_non_latch_pred_; - SearchPred *clk_pred_; - BfsFwdIterator *iter_; - MultiDrvrNetMap multi_drvr_net_map_; - bool multi_drvr_nets_found_; - // Percentage (0.0:1.0) change in delay that causes downstream - // delays to be recomputed during incremental delay calculation. - float incremental_delay_tolerance_; - - friend class FindVertexDelays; - friend class MultiDrvrNet; -}; - -} // namespace diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 0309c2c5..5e99dd7a 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -40,7 +40,7 @@ makeLumpedCapDelayCalc(StaState *sta) } LumpedCapDelayCalc::LumpedCapDelayCalc(StaState *sta) : - ArcDelayCalc(sta) + ParallelDelayCalc(sta) { } @@ -82,9 +82,9 @@ LumpedCapDelayCalc::findParasitic(const Pin *drvr_pin, Wireload *wireload = sdc_->wireload(cnst_min_max); if (wireload) { float pin_cap, wire_cap, fanout; - bool has_wire_cap; + bool has_net_load; graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, - pin_cap, wire_cap, fanout, has_wire_cap); + pin_cap, wire_cap, fanout, has_net_load); parasitic = parasitics_->estimatePiElmore(drvr_pin, rf, wireload, fanout, pin_cap, dcalc_ap->operatingConditions(), @@ -107,32 +107,8 @@ LumpedCapDelayCalc::reducedParasiticType() const return ReducedParasiticType::pi_elmore; } -void -LumpedCapDelayCalc::finishDrvrPin() -{ - for (auto parasitic : unsaved_parasitics_) - parasitics_->deleteUnsavedParasitic(parasitic); - unsaved_parasitics_.clear(); - for (auto drvr_pin : reduced_parasitic_drvrs_) - parasitics_->deleteDrvrReducedParasitics(drvr_pin); - reduced_parasitic_drvrs_.clear(); -} - -void -LumpedCapDelayCalc::inputPortDelay(const Pin *, float in_slew, - const RiseFall *rf, - const Parasitic *, - const DcalcAnalysisPt *) -{ - drvr_slew_ = in_slew; - drvr_rf_ = rf; - drvr_library_ = network_->defaultLibertyLibrary(); - multi_drvr_slew_factor_ = 1.0F; -} - float -LumpedCapDelayCalc::ceff(const LibertyCell *, - const TimingArc *, +LumpedCapDelayCalc::ceff(const TimingArc *, const Slew &, float load_cap, const Parasitic *, @@ -144,11 +120,10 @@ LumpedCapDelayCalc::ceff(const LibertyCell *, } void -LumpedCapDelayCalc::gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, +LumpedCapDelayCalc::gateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, - const Parasitic *, + const Parasitic *drvr_parasitic, float related_out_cap, const Pvt *pvt, const DcalcAnalysisPt *dcalc_ap, @@ -156,6 +131,7 @@ LumpedCapDelayCalc::gateDelay(const LibertyCell *drvr_cell, ArcDelay &gate_delay, Slew &drvr_slew) { + gateDelayInit(arc, in_slew, drvr_parasitic); GateTimingModel *model = gateModel(arc, dcalc_ap); debugPrint(debug_, "delay_calc", 3, " in_slew = %s load_cap = %s related_load_cap = %s lumped", @@ -169,7 +145,7 @@ LumpedCapDelayCalc::gateDelay(const LibertyCell *drvr_cell, // NaNs cause seg faults during table lookup. if (isnan(load_cap) || isnan(related_out_cap) || isnan(delayAsFloat(in_slew))) report_->error(710, "gate delay input variable is NaN"); - model->gateDelay(drvr_cell, pvt, in_slew1, load_cap, related_out_cap, + model->gateDelay(pvt, in_slew1, load_cap, related_out_cap, pocv_enabled_, gate_delay1, drvr_slew1); gate_delay = gate_delay1; drvr_slew = drvr_slew1; @@ -180,9 +156,6 @@ LumpedCapDelayCalc::gateDelay(const LibertyCell *drvr_cell, drvr_slew = delay_zero; drvr_slew_ = 0.0; } - drvr_rf_ = arc->toEdge()->asRiseFall(); - drvr_library_ = drvr_cell->libertyLibrary(); - multi_drvr_slew_factor_ = 1.0F; } void @@ -197,52 +170,8 @@ LumpedCapDelayCalc::loadDelay(const Pin *load_pin, load_slew = load_slew1; } -void -LumpedCapDelayCalc::thresholdAdjust(const Pin *load_pin, - ArcDelay &load_delay, - Slew &load_slew) -{ - LibertyLibrary *load_library = thresholdLibrary(load_pin); - if (load_library - && drvr_library_ - && load_library != drvr_library_) { - float drvr_vth = drvr_library_->outputThreshold(drvr_rf_); - float load_vth = load_library->inputThreshold(drvr_rf_); - float drvr_slew_delta = drvr_library_->slewUpperThreshold(drvr_rf_) - - drvr_library_->slewLowerThreshold(drvr_rf_); - float load_delay_delta = - delayAsFloat(load_slew) * ((load_vth - drvr_vth) / drvr_slew_delta); - load_delay += (drvr_rf_ == RiseFall::rise()) - ? load_delay_delta - : -load_delay_delta; - float load_slew_delta = load_library->slewUpperThreshold(drvr_rf_) - - load_library->slewLowerThreshold(drvr_rf_); - float drvr_slew_derate = drvr_library_->slewDerateFromLibrary(); - float load_slew_derate = load_library->slewDerateFromLibrary(); - load_slew = load_slew * ((load_slew_delta / load_slew_derate) - / (drvr_slew_delta / drvr_slew_derate)); - } -} - -LibertyLibrary * -LumpedCapDelayCalc::thresholdLibrary(const Pin *load_pin) -{ - if (network_->isTopLevelPort(load_pin)) - // Input/output slews use the default (first read) library - // for slew thresholds. - return network_->defaultLibertyLibrary(); - else { - LibertyPort *lib_port = network_->libertyPort(load_pin); - if (lib_port) - return lib_port->libertyCell()->libertyLibrary(); - else - return network_->defaultLibertyLibrary(); - } -} - string -LumpedCapDelayCalc::reportGateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, +LumpedCapDelayCalc::reportGateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *, @@ -254,15 +183,14 @@ LumpedCapDelayCalc::reportGateDelay(const LibertyCell *drvr_cell, GateTimingModel *model = gateModel(arc, dcalc_ap); if (model) { float in_slew1 = delayAsFloat(in_slew); - return model->reportGateDelay(drvr_cell, pvt, in_slew1, load_cap, - related_out_cap, false, digits); + return model->reportGateDelay(pvt, in_slew1, load_cap, related_out_cap, + false, digits); } return ""; } void -LumpedCapDelayCalc::checkDelay(const LibertyCell *cell, - const TimingArc *arc, +LumpedCapDelayCalc::checkDelay(const TimingArc *arc, const Slew &from_slew, const Slew &to_slew, float related_out_cap, @@ -275,16 +203,14 @@ LumpedCapDelayCalc::checkDelay(const LibertyCell *cell, if (model) { float from_slew1 = delayAsFloat(from_slew); float to_slew1 = delayAsFloat(to_slew); - model->checkDelay(cell, pvt, from_slew1, to_slew1, related_out_cap, - pocv_enabled_, margin); + model->checkDelay(pvt, from_slew1, to_slew1, related_out_cap, pocv_enabled_, margin); } else margin = delay_zero; } string -LumpedCapDelayCalc::reportCheckDelay(const LibertyCell *cell, - const TimingArc *arc, +LumpedCapDelayCalc::reportCheckDelay(const TimingArc *arc, const Slew &from_slew, const char *from_slew_annotation, const Slew &to_slew, @@ -297,16 +223,10 @@ LumpedCapDelayCalc::reportCheckDelay(const LibertyCell *cell, if (model) { float from_slew1 = delayAsFloat(from_slew); float to_slew1 = delayAsFloat(to_slew); - return model->reportCheckDelay(cell, pvt, from_slew1, from_slew_annotation, to_slew1, - related_out_cap, false, digits); + return model->reportCheckDelay(pvt, from_slew1, from_slew_annotation, + to_slew1, related_out_cap, false, digits); } return ""; } -void -LumpedCapDelayCalc::setMultiDrvrSlewFactor(float factor) -{ - multi_drvr_slew_factor_ = factor; -} - } // namespace diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index ff92dea4..db7473c0 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -16,13 +16,13 @@ #pragma once -#include "ArcDelayCalc.hh" +#include "ParallelDelayCalc.hh" namespace sta { // Liberty table model lumped capacitance arc delay calculator. // Wire delays are zero. -class LumpedCapDelayCalc : public ArcDelayCalc +class LumpedCapDelayCalc : public ParallelDelayCalc { public: LumpedCapDelayCalc(StaState *sta); @@ -31,13 +31,7 @@ public: const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) override; ReducedParasiticType reducedParasiticType() const override; - void inputPortDelay(const Pin *port_pin, - float in_slew, - const RiseFall *rf, - const Parasitic *parasitic, - const DcalcAnalysisPt *dcalc_ap) override; - void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + void gateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -47,9 +41,7 @@ public: // Return values. ArcDelay &gate_delay, Slew &drvr_slew) override; - void setMultiDrvrSlewFactor(float factor) override; - float ceff(const LibertyCell *drvr_cell, - const TimingArc *arc, + float ceff(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -60,8 +52,7 @@ public: // Return values. ArcDelay &wire_delay, Slew &load_slew) override; - void checkDelay(const LibertyCell *cell, - const TimingArc *arc, + void checkDelay(const TimingArc *arc, const Slew &from_slew, const Slew &to_slew, float related_out_cap, @@ -69,8 +60,7 @@ public: const DcalcAnalysisPt *dcalc_ap, // Return values. ArcDelay &margin) override; - string reportGateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + string reportGateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -78,8 +68,7 @@ public: const Pvt *pvt, const DcalcAnalysisPt *dcalc_ap, int digits) override; - string reportCheckDelay(const LibertyCell *cell, - const TimingArc *arc, + string reportCheckDelay(const TimingArc *arc, const Slew &from_slew, const char *from_slew_annotation, const Slew &to_slew, @@ -87,25 +76,8 @@ public: const Pvt *pvt, const DcalcAnalysisPt *dcalc_ap, int digits) override; - void finishDrvrPin() override; protected: - // Find the liberty library to use for logic/slew thresholds. - LibertyLibrary *thresholdLibrary(const Pin *load_pin); - // Adjust load_delay and load_slew from driver thresholds to load thresholds. - void thresholdAdjust(const Pin *load_pin, - ArcDelay &load_delay, - Slew &load_slew); - - Slew drvr_slew_; - float multi_drvr_slew_factor_; - const LibertyLibrary *drvr_library_; - const RiseFall *drvr_rf_; - // Parasitics returned by findParasitic that are reduced or estimated - // that can be deleted after delay calculation for the driver pin - // is finished. - Vector unsaved_parasitics_; - Vector reduced_parasitic_drvrs_; }; ArcDelayCalc * diff --git a/dcalc/ParallelDelayCalc.cc b/dcalc/ParallelDelayCalc.cc new file mode 100644 index 00000000..62fd90a6 --- /dev/null +++ b/dcalc/ParallelDelayCalc.cc @@ -0,0 +1,171 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2023, Parallax Software, Inc. +// +// 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 . + +#include "ParallelDelayCalc.hh" + +#include "TimingArc.hh" +#include "Corner.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Sdc.hh" +#include "Liberty.hh" +#include "GraphDelayCalc.hh" + +namespace sta { + +ParallelDelayCalc::ParallelDelayCalc(StaState *sta): + DelayCalcBase(sta) +{ +} + +void +ParallelDelayCalc::inputPortDelay(const Pin *drvr_pin, + float in_slew, + const RiseFall *rf, + const Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) +{ + DelayCalcBase::inputPortDelay(drvr_pin, in_slew, rf, parasitic, dcalc_ap); + multi_drvr_slew_factor_ = 1.0; +} + +void +ParallelDelayCalc::gateDelayInit(const TimingArc *arc, + const Slew &in_slew, + const Parasitic *drvr_parasitic) +{ + DelayCalcBase::gateDelayInit(arc, in_slew, drvr_parasitic); + multi_drvr_slew_factor_ = 1.0F; +} + +void +ParallelDelayCalc::findParallelGateDelays(const MultiDrvrNet *multi_drvr, + GraphDelayCalc *dcalc) +{ + int count = RiseFall::index_count * corners_->dcalcAnalysisPtCount(); + parallel_delays_.resize(count); + parallel_slews_.resize(count); + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + for (auto drvr_rf : RiseFall::range()) { + DcalcAPIndex ap_index = dcalc_ap->index(); + int drvr_rf_index = drvr_rf->index(); + int index = ap_index * RiseFall::index_count + drvr_rf_index; + findMultiDrvrGateDelay(multi_drvr, drvr_rf, dcalc_ap, dcalc, + parallel_delays_[index], + parallel_slews_[index]); + } + } +} + +void +ParallelDelayCalc::findMultiDrvrGateDelay(const MultiDrvrNet *multi_drvr, + const RiseFall *drvr_rf, + const DcalcAnalysisPt *dcalc_ap, + GraphDelayCalc *dcalc, + // Return values. + ArcDelay ¶llel_delay, + Slew ¶llel_slew) +{ + ArcDelay delay_sum = 0.0; + Slew slew_sum = 0.0; + for (Vertex *drvr_vertex : *multi_drvr->drvrs()) { + Pin *drvr_pin = drvr_vertex->pin(); + Instance *drvr_inst = network_->instance(drvr_pin); + const Pvt *pvt = sdc_->pvt(drvr_inst, dcalc_ap->constraintMinMax()); + if (pvt == nullptr) + pvt = dcalc_ap->operatingConditions(); + VertexInEdgeIterator edge_iter(drvr_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + TimingArcSet *arc_set = edge->timingArcSet(); + const LibertyPort *related_out_port = arc_set->relatedOut(); + for (TimingArc *arc : arc_set->arcs()) { + RiseFall *arc_rf = arc->toEdge()->asRiseFall(); + if (arc_rf == drvr_rf) { + Vertex *from_vertex = edge->from(graph_); + RiseFall *from_rf = arc->fromEdge()->asRiseFall(); + Slew from_slew = dcalc->edgeFromSlew(from_vertex, from_rf, + edge, dcalc_ap); + ArcDelay intrinsic_delay; + Slew intrinsic_slew; + gateDelay(arc, from_slew, 0.0, 0, 0.0, pvt, dcalc_ap, + intrinsic_delay, intrinsic_slew); + Parasitic *parasitic = findParasitic(drvr_pin, drvr_rf, dcalc_ap); + const Pin *related_out_pin = 0; + float related_out_cap = 0.0; + if (related_out_port) { + Instance *inst = network_->instance(drvr_pin); + related_out_pin = network_->findPin(inst, related_out_port); + if (related_out_pin) { + Parasitic *related_out_parasitic = findParasitic(related_out_pin, + drvr_rf, + dcalc_ap); + related_out_cap = dcalc->loadCap(related_out_pin, + related_out_parasitic, + drvr_rf, dcalc_ap); + } + } + float load_cap = dcalc->loadCap(drvr_pin, parasitic, + drvr_rf, dcalc_ap); + ArcDelay gate_delay; + Slew gate_slew; + gateDelay(arc, from_slew, load_cap, parasitic, + related_out_cap, pvt, dcalc_ap, + gate_delay, gate_slew); + delay_sum += 1.0F / (gate_delay - intrinsic_delay); + slew_sum += 1.0F / gate_slew; + } + } + } + } + parallel_delay = 1.0F / delay_sum; + parallel_slew = 1.0F / slew_sum; +} + +void +ParallelDelayCalc::parallelGateDelay(const Pin *, + const TimingArc *arc, + const Slew &from_slew, + float load_cap, + const Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &gate_slew) +{ + ArcDelay intrinsic_delay; + Slew intrinsic_slew; + gateDelay(arc, from_slew, 0.0, 0, 0.0, pvt, dcalc_ap, + intrinsic_delay, intrinsic_slew); + const RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); + int index = dcalc_ap->index() * RiseFall::index_count + drvr_rf->index(); + ArcDelay parallel_delay = parallel_delays_[index]; + Slew parallel_slew = parallel_slews_[index]; + gate_delay = parallel_delay + intrinsic_delay; + gate_slew = parallel_slew; + + Delay gate_delay1; + Slew gate_slew1; + gateDelay(arc, from_slew, load_cap, drvr_parasitic, + related_out_cap, pvt, dcalc_ap, + gate_delay1, gate_slew1); + float factor = delayRatio(gate_slew, gate_slew1); + multi_drvr_slew_factor_ = factor; +} + +} // namespace diff --git a/dcalc/ParallelDelayCalc.hh b/dcalc/ParallelDelayCalc.hh new file mode 100644 index 00000000..68510e99 --- /dev/null +++ b/dcalc/ParallelDelayCalc.hh @@ -0,0 +1,68 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2023, Parallax Software, Inc. +// +// 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 . + +#pragma once + +#include + +#include "DelayCalcBase.hh" + +namespace sta { + +// Delay calculation for parallel gates based on using parallel drive resistance. +class ParallelDelayCalc : public DelayCalcBase +{ +public: + explicit ParallelDelayCalc(StaState *sta); + void inputPortDelay(const Pin *port_pin, + float in_slew, + const RiseFall *rf, + const Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) override; + void gateDelayInit(const TimingArc *arc, + const Slew &in_slew, + const Parasitic *drvr_parasitic); + void findParallelGateDelays(const MultiDrvrNet *multi_drvr, + GraphDelayCalc *dcalc) override; + void parallelGateDelay(const Pin *drvr_pin, + const TimingArc *arc, + const Slew &from_slew, + float load_cap, + const Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &gate_slew) override; + +protected: + void findMultiDrvrGateDelay(const MultiDrvrNet *multi_drvr, + const RiseFall *drvr_rf, + const DcalcAnalysisPt *dcalc_ap, + GraphDelayCalc *dcalc, + // Return values. + ArcDelay ¶llel_delay, + Slew ¶llel_slew); + + // [drvr_rf->index][dcalc_ap->index] + vector parallel_delays_; + // [drvr_rf->index][dcalc_ap->index] + vector parallel_slews_; + float multi_drvr_slew_factor_; +}; + +} // namespace diff --git a/dcalc/RCDelayCalc.cc b/dcalc/RCDelayCalc.cc deleted file mode 100644 index 900df3f0..00000000 --- a/dcalc/RCDelayCalc.cc +++ /dev/null @@ -1,74 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2023, Parallax Software, Inc. -// -// 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 . - -#include "RCDelayCalc.hh" - -#include "Liberty.hh" -#include "Network.hh" -#include "Sdc.hh" -#include "Parasitics.hh" -#include "GraphDelayCalc.hh" - -namespace sta { - -RCDelayCalc::RCDelayCalc(StaState *sta) : - LumpedCapDelayCalc(sta) -{ -} - -ArcDelayCalc * -RCDelayCalc::copy() -{ - return new RCDelayCalc(this); -} - -void -RCDelayCalc::inputPortDelay(const Pin *, - float in_slew, - const RiseFall *rf, - const Parasitic *parasitic, - const DcalcAnalysisPt *) -{ - drvr_parasitic_ = parasitic; - drvr_slew_ = in_slew; - drvr_rf_ = rf; - drvr_cell_ = nullptr; - drvr_library_ = network_->defaultLibertyLibrary(); - multi_drvr_slew_factor_ = 1.0F; - input_port_ = true; -} - -// For DSPF on an input port the elmore delay is used as the time -// constant of an exponential waveform. The delay to the logic -// threshold and slew are computed for the exponential waveform. -// Note that this uses the driver thresholds and relies on -// thresholdAdjust to convert the delay and slew to the load's thresholds. -void -RCDelayCalc::dspfWireDelaySlew(const Pin *, - float elmore, - ArcDelay &wire_delay, - Slew &load_slew) -{ - float vth = drvr_library_->inputThreshold(drvr_rf_); - float vl = drvr_library_->slewLowerThreshold(drvr_rf_); - float vh = drvr_library_->slewUpperThreshold(drvr_rf_); - float slew_derate = drvr_library_->slewDerateFromLibrary(); - wire_delay = -elmore * log(1.0 - vth); - load_slew = (drvr_slew_ + elmore * log((1.0 - vl) / (1.0 - vh)) - / slew_derate) * multi_drvr_slew_factor_; -} - -} // namespace diff --git a/dcalc/SlewDegradeDelayCalc.cc b/dcalc/SlewDegradeDelayCalc.cc index 5e9a9f20..3e00e164 100644 --- a/dcalc/SlewDegradeDelayCalc.cc +++ b/dcalc/SlewDegradeDelayCalc.cc @@ -22,6 +22,7 @@ #include "Sdc.hh" #include "Parasitics.hh" #include "DcalcAnalysisPt.hh" +#include "LumpedCapDelayCalc.hh" namespace sta { @@ -30,33 +31,32 @@ namespace sta { // Wire delays are elmore delays. // Driver slews are degraded to loads by rise/fall transition_degradation // tables. -class SlewDegradeDelayCalc : public RCDelayCalc +class SlewDegradeDelayCalc : public LumpedCapDelayCalc { public: SlewDegradeDelayCalc(StaState *sta); - virtual ArcDelayCalc *copy(); - virtual void inputPortDelay(const Pin *port_pin, - float in_slew, - const RiseFall *rf, - const Parasitic *parasitic, - const DcalcAnalysisPt *dcalc_ap); - virtual void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *drvr_parasitic, - float related_out_cap, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew); - virtual void loadDelay(const Pin *load_pin, - ArcDelay &wire_delay, - Slew &load_slew); + ArcDelayCalc *copy() override; + void inputPortDelay(const Pin *port_pin, + float in_slew, + const RiseFall *rf, + const Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) override; + void gateDelay(const TimingArc *arc, + const Slew &in_slew, + float load_cap, + const Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) override; + void loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew) override; - using RCDelayCalc::gateDelay; - using RCDelayCalc::reportGateDelay; + using LumpedCapDelayCalc::gateDelay; + using LumpedCapDelayCalc::reportGateDelay; private: const Pvt *pvt_; @@ -69,7 +69,7 @@ makeSlewDegradeDelayCalc(StaState *sta) } SlewDegradeDelayCalc::SlewDegradeDelayCalc(StaState *sta) : - RCDelayCalc(sta) + LumpedCapDelayCalc(sta) { } @@ -87,12 +87,11 @@ SlewDegradeDelayCalc::inputPortDelay(const Pin *port_pin, const DcalcAnalysisPt *dcalc_ap) { pvt_ = dcalc_ap->operatingConditions(); - RCDelayCalc::inputPortDelay(port_pin, in_slew, rf, parasitic, dcalc_ap); + LumpedCapDelayCalc::inputPortDelay(port_pin, in_slew, rf, parasitic, dcalc_ap); } void -SlewDegradeDelayCalc::gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, +SlewDegradeDelayCalc::gateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -106,12 +105,11 @@ SlewDegradeDelayCalc::gateDelay(const LibertyCell *drvr_cell, input_port_ = false; drvr_parasitic_ = drvr_parasitic; drvr_rf_ = arc->toEdge()->asRiseFall(); - drvr_cell_ = drvr_cell; - drvr_library_ = drvr_cell->libertyLibrary(); + drvr_cell_ = arc->from()->libertyCell(); + drvr_library_ = drvr_cell_->libertyLibrary(); pvt_ = pvt; - LumpedCapDelayCalc::gateDelay(drvr_cell, arc, in_slew, - load_cap, drvr_parasitic, related_out_cap, - pvt, dcalc_ap, + LumpedCapDelayCalc::gateDelay(arc, in_slew, load_cap, drvr_parasitic, + related_out_cap, pvt, dcalc_ap, gate_delay, drvr_slew); } @@ -129,8 +127,7 @@ SlewDegradeDelayCalc::loadDelay(const Pin *load_pin, if (elmore_exists) { if (drvr_library_ && drvr_library_->wireSlewDegradationTable(drvr_rf_)) { wire_delay1 = elmore; - load_slew1 = drvr_library_->degradeWireSlew(drvr_cell_, drvr_rf_, - pvt_, + load_slew1 = drvr_library_->degradeWireSlew(drvr_rf_, delayAsFloat(drvr_slew_), delayAsFloat(wire_delay1)); } diff --git a/dcalc/SlewDegradeDelayCalc.hh b/dcalc/SlewDegradeDelayCalc.hh index 97b39c5f..7195985d 100644 --- a/dcalc/SlewDegradeDelayCalc.hh +++ b/dcalc/SlewDegradeDelayCalc.hh @@ -16,10 +16,11 @@ #pragma once -#include "RCDelayCalc.hh" - namespace sta { +class ArcDelayCalc; +class StaState; + ArcDelayCalc * makeSlewDegradeDelayCalc(StaState *sta); diff --git a/dcalc/UnitDelayCalc.cc b/dcalc/UnitDelayCalc.cc index 20ddaf14..cc629ba5 100644 --- a/dcalc/UnitDelayCalc.cc +++ b/dcalc/UnitDelayCalc.cc @@ -61,8 +61,7 @@ UnitDelayCalc::inputPortDelay(const Pin *, } void -UnitDelayCalc::gateDelay(const LibertyCell *, - const TimingArc *, +UnitDelayCalc::gateDelay(const TimingArc *, const Slew &, float, const Parasitic *, @@ -75,6 +74,29 @@ UnitDelayCalc::gateDelay(const LibertyCell *, drvr_slew = 0.0; } +void +UnitDelayCalc::findParallelGateDelays(const MultiDrvrNet *, + GraphDelayCalc *) +{ +} + +void +UnitDelayCalc::parallelGateDelay(const Pin *, + const TimingArc *, + const Slew &, + float, + const Parasitic *, + float, + const Pvt *, + const DcalcAnalysisPt *, + // Return values. + ArcDelay &gate_delay, + Slew &gate_slew) +{ + gate_delay = units_->timeUnit()->scale(); + gate_slew = 0.0; +} + void UnitDelayCalc::loadDelay(const Pin *, ArcDelay &wire_delay, @@ -85,8 +107,7 @@ UnitDelayCalc::loadDelay(const Pin *, } float -UnitDelayCalc::ceff(const LibertyCell *, - const TimingArc *, +UnitDelayCalc::ceff(const TimingArc *, const Slew &, float, const Parasitic *, @@ -98,8 +119,7 @@ UnitDelayCalc::ceff(const LibertyCell *, } string -UnitDelayCalc::reportGateDelay(const LibertyCell *, - const TimingArc *, +UnitDelayCalc::reportGateDelay(const TimingArc *, const Slew &, float, const Parasitic *, @@ -114,8 +134,7 @@ UnitDelayCalc::reportGateDelay(const LibertyCell *, } void -UnitDelayCalc::checkDelay(const LibertyCell *, - const TimingArc *, +UnitDelayCalc::checkDelay(const TimingArc *, const Slew &, const Slew &, float, @@ -128,8 +147,7 @@ UnitDelayCalc::checkDelay(const LibertyCell *, } string -UnitDelayCalc::reportCheckDelay(const LibertyCell *, - const TimingArc *, +UnitDelayCalc::reportCheckDelay(const TimingArc *, const Slew &, const char *, const Slew &, diff --git a/dcalc/UnitDelayCalc.hh b/dcalc/UnitDelayCalc.hh index 1b7b22b9..6ecae7cf 100644 --- a/dcalc/UnitDelayCalc.hh +++ b/dcalc/UnitDelayCalc.hh @@ -35,8 +35,7 @@ public: const RiseFall *rf, const Parasitic *parasitic, const DcalcAnalysisPt *dcalc_ap) override; - void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + void gateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -46,21 +45,32 @@ public: // Return values. ArcDelay &gate_delay, Slew &drvr_slew) override; + void findParallelGateDelays(const MultiDrvrNet *multi_drvr, + GraphDelayCalc *dcalc) override; + // Retrieve the delay and slew for one parallel gate. + void parallelGateDelay(const Pin *drvr_pin, + const TimingArc *arc, + const Slew &from_slew, + float load_cap, + const Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &gate_slew) override; void loadDelay(const Pin *load_pin, // Return values. ArcDelay &wire_delay, Slew &load_slew) override; - void setMultiDrvrSlewFactor(float) override {} - float ceff(const LibertyCell *drvr_cell, - const TimingArc *arc, + float ceff(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, float related_out_cap, const Pvt *pvt, const DcalcAnalysisPt *dcalc_ap) override; - void checkDelay(const LibertyCell *cell, - const TimingArc *arc, + void checkDelay(const TimingArc *arc, const Slew &from_slew, const Slew &to_slew, float related_out_cap, @@ -68,8 +78,7 @@ public: const DcalcAnalysisPt *dcalc_ap, // Return values. ArcDelay &margin) override; - string reportGateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + string reportGateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -77,8 +86,7 @@ public: const Pvt *pvt, const DcalcAnalysisPt *dcalc_ap, int digits) override; - string reportCheckDelay(const LibertyCell *cell, - const TimingArc *arc, + string reportCheckDelay(const TimingArc *arc, const Slew &from_slew, const char *from_slew_annotation, const Slew &to_slew, diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index af4df1f2..95e9a184 100644 Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ diff --git a/graph/Graph.cc b/graph/Graph.cc index 8d5bc4ee..29af550a 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -783,8 +783,8 @@ Graph::setWireArcDelay(Edge *edge, } bool -Graph::arcDelayAnnotated(Edge *edge, - TimingArc *arc, +Graph::arcDelayAnnotated(const Edge *edge, + const TimingArc *arc, DcalcAPIndex ap_index) const { if (arc_delay_annotated_.size()) { @@ -799,7 +799,7 @@ Graph::arcDelayAnnotated(Edge *edge, void Graph::setArcDelayAnnotated(Edge *edge, - TimingArc *arc, + const TimingArc *arc, DcalcAPIndex ap_index, bool annotated) { diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index 37ed4baf..c1c6617b 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -17,6 +17,7 @@ #pragma once #include +#include #include "MinMax.hh" #include "LibertyClass.hh" @@ -28,22 +29,26 @@ namespace sta { using std::string; +using std::vector; class Parasitic; class DcalcAnalysisPt; +class MultiDrvrNet; // Delay calculator class hierarchy. // ArcDelayCalc // UnitDelayCalc -// LumpedCapDelayCalc -// RCDelayCalc -// SlewDegradeDelayCalc -// DmpCeffDelayCalc -// DmpCeffElmoreDelayCalc -// DmpCeffTwoPoleDelayCalc -// ArnoldiDelayCalc +// DelayCalcBase +// ParallelDelayCalc +// LumpedCapDelayCalc +// SlewDegradeDelayCalc +// DmpCeffDelayCalc +// DmpCeffElmoreDelayCalc +// DmpCeffTwoPoleDelayCalc +// ArnoldiDelayCalc -// Abstract class to interface to a delay calculator primitive. +// Abstract class for the graph delay calculator traversal to interface +// to a delay calculator primitive. class ArcDelayCalc : public StaState { public: @@ -66,8 +71,7 @@ public: const DcalcAnalysisPt *dcalc_ap) = 0; // Find the delay and slew for arc driving drvr_pin. - virtual void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + virtual void gateDelay(const TimingArc *arc, const Slew &in_slew, // Pass in load_cap or drvr_parasitic. float load_cap, @@ -78,16 +82,29 @@ public: // Return values. ArcDelay &gate_delay, Slew &drvr_slew) = 0; + // Find gate delays and slews for parallel gates. + virtual void findParallelGateDelays(const MultiDrvrNet *multi_drvr, + GraphDelayCalc *dcalc) = 0; + // Retrieve the delay and slew for one parallel gate. + virtual void parallelGateDelay(const Pin *drvr_pin, + const TimingArc *arc, + const Slew &from_slew, + float load_cap, + const Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &gate_slew) = 0; // Find the wire delay and load slew of a load pin. // Called after inputPortDelay or gateDelay. virtual void loadDelay(const Pin *load_pin, // Return values. ArcDelay &wire_delay, Slew &load_slew) = 0; - virtual void setMultiDrvrSlewFactor(float factor) = 0; // Ceff for parasitics with pi models. - virtual float ceff(const LibertyCell *drvr_cell, - const TimingArc *arc, + virtual float ceff(const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *drvr_parasitic, @@ -97,8 +114,7 @@ public: // Find the delay for a timing check arc given the arc's // from/clock, to/data slews and related output pin parasitic. - virtual void checkDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + virtual void checkDelay(const TimingArc *arc, const Slew &from_slew, const Slew &to_slew, float related_out_cap, @@ -107,8 +123,7 @@ public: // Return values. ArcDelay &margin) = 0; // Report delay and slew calculation. - virtual string reportGateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, + virtual string reportGateDelay(const TimingArc *arc, const Slew &in_slew, // Pass in load_cap or drvr_parasitic. float load_cap, @@ -118,8 +133,7 @@ public: const DcalcAnalysisPt *dcalc_ap, int digits) = 0; // Report timing check delay calculation. - virtual string reportCheckDelay(const LibertyCell *cell, - const TimingArc *arc, + virtual string reportCheckDelay(const TimingArc *arc, const Slew &from_slew, const char *from_slew_annotation, const Slew &to_slew, diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 247b5bcf..d2dea625 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -157,11 +157,11 @@ public: DcalcAPIndex ap_index, const ArcDelay &delay); // Is timing arc delay annotated. - bool arcDelayAnnotated(Edge *edge, - TimingArc *arc, + bool arcDelayAnnotated(const Edge *edge, + const TimingArc *arc, DcalcAPIndex ap_index) const; void setArcDelayAnnotated(Edge *edge, - TimingArc *arc, + const TimingArc *arc, DcalcAPIndex ap_index, bool annotated); bool wireDelayAnnotated(Edge *edge, @@ -539,7 +539,6 @@ class VertexSet : public Set { public: VertexSet(Graph *&graph); - VertexSet(const VertexSet &set); }; } // namespace diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 5d795979..da47b31e 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -16,58 +16,66 @@ #pragma once -#include +#include +#include "Map.hh" +#include "NetworkClass.hh" #include "GraphClass.hh" +#include "SearchClass.hh" #include "DcalcAnalysisPt.hh" #include "StaState.hh" +#include "Delay.hh" namespace sta { -using std::string; +using std::vector; -class BfsFwdIterator; -class SearchPred; class DelayCalcObserver; -class Parasitic; -class Corner; +class MultiDrvrNet; +class FindVertexDelays; +class NetCaps; -// Base class for graph delay calculator. -// This class annotates the arc delays and slews on the graph by calling -// the timing arc delay calculation primitive through an implementation -// of the ArcDelayCalc abstract class. -// This class does not traverse the graph or call an arc delay -// calculator. Use it with applications that use an external delay -// calculator and annotate all edge delays. +typedef Map MultiDrvrNetMap; + +// This class traverses the graph calling the arc delay calculator and +// annotating delays on graph edges. class GraphDelayCalc : public StaState { public: - explicit GraphDelayCalc(StaState *sta); - virtual ~GraphDelayCalc() {} - // Find arc delays and vertex slews thru level. - virtual void findDelays(Level /* level */) {}; - // Find and annotate drvr_vertex gate and load delays/slews. - virtual void findDelays(Vertex * /* drvr_vertex */) {}; + GraphDelayCalc(StaState *sta); + virtual ~GraphDelayCalc(); + virtual void copyState(const StaState *sta); + // Set the observer for edge delay changes. + virtual void setObserver(DelayCalcObserver *observer); // Invalidate all delays/slews. - virtual void delaysInvalid() {}; + virtual void delaysInvalid(); // Invalidate vertex and downstream delays/slews. - virtual void delayInvalid(Vertex * /* vertex */) {}; - virtual void delayInvalid(const Pin * /* pin */) {}; - virtual void deleteVertexBefore(Vertex * /* vertex */) {}; + virtual void delayInvalid(Vertex *vertex); + virtual void delayInvalid(const Pin *pin); + virtual void deleteVertexBefore(Vertex *vertex); // Reset to virgin state. - virtual void clear() {} + virtual void clear(); + // Find arc delays and vertex slews thru level. + virtual void findDelays(Level level); + // Find and annotate drvr_vertex gate and load delays/slews. + virtual void findDelays(Vertex *drvr_vertex); // Returned string is owned by the caller. - virtual string reportDelayCalc(Edge *edge, - TimingArc *arc, + virtual string reportDelayCalc(const Edge *edge, + const TimingArc *arc, const Corner *corner, const MinMax *min_max, int digits); // Percentage (0.0:1.0) change in delay that causes downstream // delays to be recomputed during incremental delay calculation. virtual float incrementalDelayTolerance(); - virtual void setIncrementalDelayTolerance(float /* tol */) {} - // Set the observer for edge delay changes. - virtual void setObserver(DelayCalcObserver *observer); + virtual void setIncrementalDelayTolerance(float tol); + // Load pin_cap + wire_cap. + virtual float loadCap(const Pin *drvr_pin, + const RiseFall *drvr_rf, + const DcalcAnalysisPt *dcalc_ap) const; + // Load pin_cap + wire_cap including parasitic min/max for rise/fall. + virtual float loadCap(const Pin *drvr_pin, + const DcalcAnalysisPt *dcalc_ap) const; // pin_cap = net pin capacitances + port external pin capacitance, // wire_cap = annotated net capacitance + port external wire capacitance. virtual void loadCap(const Pin *drvr_pin, @@ -78,17 +86,15 @@ public: float &pin_cap, float &wire_cap) const; // Load pin_cap + wire_cap including parasitic. - virtual float loadCap(const Pin *drvr_pin, - const RiseFall *to_rf, - const DcalcAnalysisPt *dcalc_ap) const; - // Load pin_cap + wire_cap including parasitic min/max for rise/fall. - virtual float loadCap(const Pin *drvr_pin, - const DcalcAnalysisPt *dcalc_ap) const; - // Load pin_cap + wire_cap. virtual float loadCap(const Pin *drvr_pin, const Parasitic *drvr_parasitic, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) const; + float loadCap(const Pin *drvr_pin, + const Parasitic *drvr_parasitic, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap, + const MultiDrvrNet *multi_drvr) const; virtual void netCaps(const Pin *drvr_pin, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap, @@ -97,9 +103,9 @@ public: float &wire_cap, float &fanout, bool &has_set_load) const; - virtual float ceff(Edge *edge, - TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap); + float ceff(Edge *edge, + TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap); // Precedence: // SDF annotation // Liberty library @@ -118,6 +124,128 @@ public: // Return values. float &min_period, bool &exists); + + Slew edgeFromSlew(const Vertex *from_vertex, + const RiseFall *from_rf, + const Edge *edge, + const DcalcAnalysisPt *dcalc_ap); + +protected: + void seedInvalidDelays(); + void initSlew(Vertex *vertex); + void seedRootSlew(Vertex *vertex, + ArcDelayCalc *arc_delay_calc); + void seedRootSlews(); + void seedDrvrSlew(Vertex *vertex, + ArcDelayCalc *arc_delay_calc); + void seedNoDrvrSlew(Vertex *drvr_vertex, + const Pin *drvr_pin, + const RiseFall *rf, + DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc); + void seedNoDrvrCellSlew(Vertex *drvr_vertex, + const Pin *drvr_pin, + const RiseFall *rf, + InputDrive *drive, + DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc); + void seedLoadSlew(Vertex *vertex); + void setInputPortWireDelays(Vertex *vertex); + void findInputDriverDelay(const LibertyCell *drvr_cell, + const Pin *drvr_pin, + Vertex *drvr_vertex, + const RiseFall *rf, + const LibertyPort *from_port, + float *from_slews, + const LibertyPort *to_port, + const DcalcAnalysisPt *dcalc_ap); + LibertyPort *driveCellDefaultFromPort(const LibertyCell *cell, + const LibertyPort *to_port); + int findPortIndex(const LibertyCell *cell, + const LibertyPort *port); + void findInputArcDelay(const Pin *drvr_pin, + Vertex *drvr_vertex, + const TimingArc *arc, + float from_slew, + const DcalcAnalysisPt *dcalc_ap); + bool findDriverDelays(Vertex *drvr_vertex, + ArcDelayCalc *arc_delay_calc); + MultiDrvrNet *findMultiDrvrNet(Vertex *drvr_pin); + MultiDrvrNet *makeMultiDrvrNet(PinSet &drvr_pins); + bool findDriverDelays1(Vertex *drvr_vertex, + MultiDrvrNet *multi_drvr, + ArcDelayCalc *arc_delay_calc); + void initLoadSlews(Vertex *drvr_vertex); + bool findDriverEdgeDelays(const Instance *drvr_inst, + const Pin *drvr_pin, + Vertex *drvr_vertex, + const MultiDrvrNet *multi_drvr, + Edge *edge, + ArcDelayCalc *arc_delay_calc); + void initWireDelays(Vertex *drvr_vertex); + void initRootSlews(Vertex *vertex); + void zeroSlewAndWireDelays(Vertex *drvr_vertex); + void findVertexDelay(Vertex *vertex, + ArcDelayCalc *arc_delay_calc, + bool propagate); + void enqueueTimingChecksEdges(Vertex *vertex); + bool findArcDelay(const Pin *drvr_pin, + Vertex *drvr_vertex, + const TimingArc *arc, + const Parasitic *drvr_parasitic, + float related_out_cap, + Vertex *from_vertex, + Edge *edge, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + const MultiDrvrNet *multi_drvr, + ArcDelayCalc *arc_delay_calc); + void annotateLoadDelays(Vertex *drvr_vertex, + const RiseFall *drvr_rf, + const ArcDelay &extra_delay, + bool merge, + const DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc); + void findLatchEdgeDelays(Edge *edge); + void findCheckEdgeDelays(Edge *edge, + ArcDelayCalc *arc_delay_calc); + void deleteMultiDrvrNets(); + Slew checkEdgeClkSlew(const Vertex *from_vertex, + const RiseFall *from_rf, + const DcalcAnalysisPt *dcalc_ap); + bool bidirectDrvrSlewFromLoad(const Vertex *vertex) const; + MultiDrvrNet *multiDrvrNet(const Vertex *drvr_vertex) const; + void loadCap(const Parasitic *drvr_parasitic, + bool has_set_load, + // Return values. + float &pin_cap, + float &wire_cap) const; + + // Observer for edge delay changes. + DelayCalcObserver *observer_; + bool delays_seeded_; + bool incremental_; + bool delays_exist_; + // Vertices with invalid -to delays. + VertexSet *invalid_delays_; + // Timing check edges with invalid delays. + EdgeSet invalid_check_edges_; + // Latch D->Q edges with invalid delays. + EdgeSet invalid_latch_edges_; + // shared by invalid_check_edges_ and invalid_latch_edges_ + std::mutex invalid_edge_lock_; + SearchPred *search_pred_; + SearchPred *search_non_latch_pred_; + SearchPred *clk_pred_; + BfsFwdIterator *iter_; + MultiDrvrNetMap multi_drvr_net_map_; + bool multi_drvr_nets_found_; + // Percentage (0.0:1.0) change in delay that causes downstream + // delays to be recomputed during incremental delay calculation. + float incremental_delay_tolerance_; + + friend class FindVertexDelays; + friend class MultiDrvrNet; }; // Abstract base class for edge delay change observer. @@ -131,4 +259,33 @@ public: virtual void checkDelayChangedTo(Vertex *vertex) = 0; }; +// Nets with multiple drivers (tristate, bidirect or output). +// Cache net caps to prevent N^2 net pin walk. +class MultiDrvrNet +{ +public: + MultiDrvrNet(VertexSet *drvrs); + ~MultiDrvrNet(); + const VertexSet *drvrs() const { return drvrs_; } + VertexSet *drvrs() { return drvrs_; } + bool parallelGates(const Network *network) const; + Vertex *dcalcDrvr() const { return dcalc_drvr_; } + void setDcalcDrvr(Vertex *drvr); + void netCaps(const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load) const; + void findCaps(const Sdc *sdc); + +private: + // Driver that triggers delay calculation for all the drivers on the net. + Vertex *dcalc_drvr_; + VertexSet *drvrs_; + // [drvr_rf->index][dcalc_ap->index] + vector net_caps_; +}; + } // namespace diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 0924ebd9..14dab380 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -167,16 +167,14 @@ public: const LibertyCell *cell, const Pvt *pvt) const; float scaleFactor(ScaleFactorType type, - int tr_index, + int rf_index, const LibertyCell *cell, const Pvt *pvt) const; void setWireSlewDegradationTable(TableModel *model, RiseFall *rf); TableModel *wireSlewDegradationTable(const RiseFall *rf) const; - float degradeWireSlew(const LibertyCell *cell, - const RiseFall *rf, - const Pvt *pvt, + float degradeWireSlew(const RiseFall *rf, float in_slew, float wire_delay) const; // Check for supported axis variables. @@ -323,9 +321,7 @@ public: void addDriverWaveform(DriverWaveform *driver_waveform); protected: - float degradeWireSlew(const LibertyCell *cell, - const Pvt *pvt, - const TableModel *model, + float degradeWireSlew(const TableModel *model, float in_slew, float wire_delay) const; @@ -925,7 +921,7 @@ public: RiseFall *rf); float scale(ScaleFactorType type, ScaleFactorPvt pvt, - int tr_index); + int rf_index); float scale(ScaleFactorType type, ScaleFactorPvt pvt); void setScale(ScaleFactorType type, diff --git a/include/sta/LinearModel.hh b/include/sta/LinearModel.hh index f06558e0..25f1988c 100644 --- a/include/sta/LinearModel.hh +++ b/include/sta/LinearModel.hh @@ -23,9 +23,10 @@ namespace sta { class GateLinearModel : public GateTimingModel { public: - GateLinearModel(float intrinsic, float resistance); - void gateDelay(const LibertyCell *cell, - const Pvt *pvt, + GateLinearModel(LibertyCell *cell, + float intrinsic, + float resistance); + void gateDelay(const Pvt *pvt, float in_slew, float load_cap, float related_out_cap, @@ -33,15 +34,13 @@ public: // Return values. ArcDelay &gate_delay, Slew &drvr_slew) const override; - string reportGateDelay(const LibertyCell *cell, - const Pvt *pvt, + string reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, float related_out_cap, bool pocv_enabled, int digits) const override; - float driveResistance(const LibertyCell *cell, - const Pvt *pvt) const override; + float driveResistance(const Pvt *pvt) const override; protected: void setIsScaled(bool is_scaled) override; @@ -53,17 +52,16 @@ protected: class CheckLinearModel : public CheckTimingModel { public: - explicit CheckLinearModel(float intrinsic); - void checkDelay(const LibertyCell *cell, - const Pvt *pvt, + explicit CheckLinearModel(LibertyCell *cell, + float intrinsic); + void checkDelay(const Pvt *pvt, float from_slew, float to_slew, float related_out_cap, bool pocv_enabled, // Return values. ArcDelay &margin) const override; - string reportCheckDelay(const LibertyCell *cell, - const Pvt *pvt, + string reportCheckDelay(const Pvt *pvt, float from_slew, const char *from_slew_annotation, float to_slew, diff --git a/include/sta/PowerClass.hh b/include/sta/PowerClass.hh index a46fe5a8..57c9ad0a 100644 --- a/include/sta/PowerClass.hh +++ b/include/sta/PowerClass.hh @@ -23,6 +23,7 @@ enum class PwrActivityOrigin global, input, user, + vcd, propagated, clock, constant, @@ -42,6 +43,7 @@ public: float duty() const { return duty_; } void setDuty(float duty); PwrActivityOrigin origin() { return origin_; } + void setOrigin(PwrActivityOrigin origin); const char *originName() const; void set(float activity, float duty, diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index c6ddeaef..eb471f20 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -827,7 +827,8 @@ public: // Returns nullptr if set_operating_conditions has not been called. OperatingConditions *operatingConditions(const MinMax *min_max); // Instance specific process/voltage/temperature. - const Pvt *pvt(Instance *inst, const MinMax *min_max) const; + const Pvt *pvt(const Instance *inst, + const MinMax *min_max) const; // Pvt may be shared among multiple instances. void setPvt(const Instance *inst, const MinMaxAll *min_max, diff --git a/include/sta/SearchClass.hh b/include/sta/SearchClass.hh index 6aea164f..17d67618 100644 --- a/include/sta/SearchClass.hh +++ b/include/sta/SearchClass.hh @@ -59,6 +59,8 @@ class MinPulseWidthCheck; class MinPeriodCheck; class MaxSkewCheck; class CharPtrLess; +class SearchPred; +class BfsFwdIterator; // Tag compare using tag matching (tagMatch) critera. class TagMatchLess @@ -114,7 +116,7 @@ typedef Vector PathVertexSeq; typedef Vector SlackSeq; typedef Delay Crpr; typedef Vector PathRefSeq; -typedef MinMaxValues ClkDelays[RiseFall::index_count][RiseFall::index_count]; +typedef MinMaxValues ClkDelays[RiseFall::index_count][RiseFall::index_count]; enum class ReportPathFormat { full, full_clock, diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 998ca562..56344d09 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -429,8 +429,6 @@ public: bool isDisabledConstant(Edge *edge); // Edge is default cond disabled by timing_disable_cond_default_arcs var. bool isDisabledCondDefault(Edge *edge); - // Edge is disabled to prpath a clock from propagating. - bool isDisabledClock(Edge *edge); // Return a set of constant pins that disabled edge. // Caller owns the returned set. PinSet disabledConstantPins(Edge *edge); diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 0ec14588..81b03895 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -51,15 +51,15 @@ tableVariableUnit(TableAxisVariable variable, class GateTableModel : public GateTimingModel { public: - GateTableModel(TableModel *delay_model, + GateTableModel(LibertyCell *cell, + TableModel *delay_model, TableModel *delay_sigma_models[EarlyLate::index_count], TableModel *slew_model, TableModel *slew_sigma_models[EarlyLate::index_count], ReceiverModelPtr receiver_model, OutputWaveforms *output_waveforms); virtual ~GateTableModel(); - void gateDelay(const LibertyCell *cell, - const Pvt *pvt, + void gateDelay(const Pvt *pvt, float in_slew, float load_cap, float related_out_cap, @@ -67,15 +67,13 @@ public: // Return values. ArcDelay &gate_delay, Slew &drvr_slew) const override; - string reportGateDelay(const LibertyCell *cell, - const Pvt *pvt, + string reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, float related_out_cap, bool pocv_enabled, int digits) const override; - float driveResistance(const LibertyCell *cell, - const Pvt *pvt) const override; + float driveResistance(const Pvt *pvt) const override; const TableModel *delayModel() const { return delay_model_; } const TableModel *slewModel() const { return slew_model_; } @@ -86,8 +84,7 @@ public: static bool checkAxes(const TablePtr table); protected: - void maxCapSlew(const LibertyCell *cell, - float in_slew, + void maxCapSlew(float in_slew, const Pvt *pvt, float &slew, float &cap) const; @@ -96,16 +93,12 @@ protected: float load_cap, float in_slew, float related_out_cap) const; - float findValue(const LibertyLibrary *library, - const LibertyCell *cell, - const Pvt *pvt, + float findValue(const Pvt *pvt, const TableModel *model, float in_slew, float load_cap, float related_out_cap) const; string reportTableLookup(const char *result_name, - const LibertyLibrary *library, - const LibertyCell *cell, const Pvt *pvt, const TableModel *model, float in_slew, @@ -133,19 +126,18 @@ protected: class CheckTableModel : public CheckTimingModel { public: - explicit CheckTableModel(TableModel *model, + explicit CheckTableModel(LibertyCell *cell, + TableModel *model, TableModel *sigma_models[EarlyLate::index_count]); virtual ~CheckTableModel(); - void checkDelay(const LibertyCell *cell, - const Pvt *pvt, + void checkDelay(const Pvt *pvt, float from_slew, float to_slew, float related_out_cap, bool pocv_enabled, // Return values. ArcDelay &margin) const override; - string reportCheckDelay(const LibertyCell *cell, - const Pvt *pvt, + string reportCheckDelay(const Pvt *pvt, float from_slew, const char *from_slew_annotation, float to_slew, @@ -160,9 +152,7 @@ public: protected: void setIsScaled(bool is_scaled) override; - float findValue(const LibertyLibrary *library, - const LibertyCell *cell, - const Pvt *pvt, + float findValue(const Pvt *pvt, const TableModel *model, float from_slew, float to_slew, @@ -179,8 +169,6 @@ protected: float in_slew, float related_out_cap) const; string reportTableDelay(const char *result_name, - const LibertyLibrary *library, - const LibertyCell *cell, const Pvt *pvt, const TableModel *model, float from_slew, @@ -217,14 +205,12 @@ public: float value2, float value3) const; // Table interpolated lookup with scale factor. - float findValue(const LibertyLibrary *library, - const LibertyCell *cell, + float findValue(const LibertyCell *cell, const Pvt *pvt, float value1, float value2, float value3) const; string reportValue(const char *result_name, - const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, float value1, @@ -237,11 +223,9 @@ public: Report *report) const; protected: - float scaleFactor(const LibertyLibrary *library, - const LibertyCell *cell, + float scaleFactor(const LibertyCell *cell, const Pvt *pvt) const; - string reportPvtScaleFactor(const LibertyLibrary *library, - const LibertyCell *cell, + string reportPvtScaleFactor(const LibertyCell *cell, const Pvt *pvt, int digits) const; @@ -280,7 +264,6 @@ public: float axis_value2, float axis_value3) const; virtual string reportValue(const char *result_name, - const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, float value1, @@ -306,7 +289,6 @@ public: float axis_value2, float axis_value3) const override; string reportValue(const char *result_name, - const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, float value1, @@ -342,7 +324,6 @@ public: float value2, float value3) const override; string reportValue(const char *result_name, - const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, float value1, @@ -389,7 +370,6 @@ public: float value2, float value3) const override; string reportValue(const char *result_name, - const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, float value1, @@ -436,7 +416,6 @@ public: float value2, float value3) const override; string reportValue(const char *result_name, - const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, float value1, diff --git a/include/sta/TimingModel.hh b/include/sta/TimingModel.hh index 94604d78..e6b2fd04 100644 --- a/include/sta/TimingModel.hh +++ b/include/sta/TimingModel.hh @@ -29,21 +29,21 @@ using std::string; class TimingModel { public: + TimingModel(LibertyCell *cell); virtual ~TimingModel() {} - -protected: virtual void setIsScaled(bool is_scaled) = 0; - friend class LibertyCell; +protected: + LibertyCell *cell_; }; // Abstract base class for LinearModel and TableModel. class GateTimingModel : public TimingModel { public: + GateTimingModel(LibertyCell *cell); // Gate delay calculation. - virtual void gateDelay(const LibertyCell *cell, - const Pvt *pvt, + virtual void gateDelay(const Pvt *pvt, float in_slew, float load_cap, float related_out_cap, @@ -51,32 +51,29 @@ public: // Return values. ArcDelay &gate_delay, Slew &drvr_slew) const = 0; - virtual string reportGateDelay(const LibertyCell *cell, - const Pvt *pvt, + virtual string reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, float related_out_cap, bool pocv_enabled, int digits) const = 0; - virtual float driveResistance(const LibertyCell *cell, - const Pvt *pvt) const = 0; + virtual float driveResistance(const Pvt *pvt) const = 0; }; // Abstract base class for timing check timing models. class CheckTimingModel : public TimingModel { public: + CheckTimingModel(LibertyCell *cell); // Timing check margin delay calculation. - virtual void checkDelay(const LibertyCell *cell, - const Pvt *pvt, + virtual void checkDelay(const Pvt *pvt, float from_slew, float to_slew, float related_out_cap, bool pocv_enabled, // Return values. ArcDelay &margin) const = 0; - virtual string reportCheckDelay(const LibertyCell *cell, - const Pvt *pvt, + virtual string reportCheckDelay(const Pvt *pvt, float from_slew, const char *from_slew_annotation, float to_slew, diff --git a/liberty/InternalPower.cc b/liberty/InternalPower.cc index efa6afce..4c251eb4 100644 --- a/liberty/InternalPower.cc +++ b/liberty/InternalPower.cc @@ -131,9 +131,7 @@ InternalPowerModel::power(const LibertyCell *cell, float axis_value1, axis_value2, axis_value3; findAxisValues(in_slew, load_cap, axis_value1, axis_value2, axis_value3); - const LibertyLibrary *library = cell->libertyLibrary(); - return model_->findValue(library, cell, pvt, - axis_value1, axis_value2, axis_value3); + return model_->findValue(cell, pvt, axis_value1, axis_value2, axis_value3); } else return 0.0; @@ -151,8 +149,8 @@ InternalPowerModel::reportPower(const LibertyCell *cell, findAxisValues(in_slew, load_cap, axis_value1, axis_value2, axis_value3); const LibertyLibrary *library = cell->libertyLibrary(); - return model_->reportValue("Power", library, cell, pvt, - axis_value1, nullptr, axis_value2, axis_value3, + return model_->reportValue("Power", cell, pvt, axis_value1, nullptr, + axis_value2, axis_value3, library->units()->powerUnit(), digits); } return ""; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index aedcb5b6..6e8a91c0 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -94,12 +94,12 @@ LibertyLibrary::LibertyLibrary(const char *name, addTableTemplate(scalar_template, type); } - for (auto tr_index : RiseFall::rangeIndex()) { - wire_slew_degradation_tbls_[tr_index] = nullptr; - input_threshold_[tr_index] = input_threshold_default_; - output_threshold_[tr_index] = output_threshold_default_; - slew_lower_threshold_[tr_index] = slew_lower_threshold_default_; - slew_upper_threshold_[tr_index] = slew_upper_threshold_default_; + for (auto rf_index : RiseFall::rangeIndex()) { + wire_slew_degradation_tbls_[rf_index] = nullptr; + input_threshold_[rf_index] = input_threshold_default_; + output_threshold_[rf_index] = output_threshold_default_; + slew_lower_threshold_[rf_index] = slew_lower_threshold_default_; + slew_upper_threshold_[rf_index] = slew_upper_threshold_default_; } } @@ -111,8 +111,8 @@ LibertyLibrary::~LibertyLibrary() scale_factors_map_.deleteContents(); delete scale_factors_; - for (auto tr_index : RiseFall::rangeIndex()) { - TableModel *model = wire_slew_degradation_tbls_[tr_index]; + for (auto rf_index : RiseFall::rangeIndex()) { + TableModel *model = wire_slew_degradation_tbls_[rf_index]; delete model; } operating_conditions_.deleteContents(); @@ -289,7 +289,7 @@ LibertyLibrary::scaleFactor(ScaleFactorType type, float LibertyLibrary::scaleFactor(ScaleFactorType type, - int tr_index, + int rf_index, const LibertyCell *cell, const Pvt *pvt) const { @@ -299,18 +299,18 @@ LibertyLibrary::scaleFactor(ScaleFactorType type, // All scale factors are unity for nominal pvt. if (pvt) { ScaleFactors *scale_factors = nullptr; - // Cell level scale factors have precidence over library scale factors. + // Cell level scale factors have precedence over library scale factors. if (cell) scale_factors = cell->scaleFactors(); if (scale_factors == nullptr) scale_factors = scale_factors_; if (scale_factors) { float process_scale = 1.0F + (pvt->process() - nominal_process_) - * scale_factors->scale(type, ScaleFactorPvt::process, tr_index); + * scale_factors->scale(type, ScaleFactorPvt::process, rf_index); float temp_scale = 1.0F + (pvt->temperature() - nominal_temperature_) - * scale_factors->scale(type, ScaleFactorPvt::temp, tr_index); + * scale_factors->scale(type, ScaleFactorPvt::temp, rf_index); float volt_scale = 1.0F + (pvt->voltage() - nominal_voltage_) - * scale_factors->scale(type, ScaleFactorPvt::volt, tr_index); + * scale_factors->scale(type, ScaleFactorPvt::volt, rf_index); float scale = process_scale * temp_scale * volt_scale; return scale; } @@ -322,10 +322,10 @@ void LibertyLibrary::setWireSlewDegradationTable(TableModel *model, RiseFall *rf) { - int tr_index = rf->index(); - if (wire_slew_degradation_tbls_[tr_index]) - delete wire_slew_degradation_tbls_[tr_index]; - wire_slew_degradation_tbls_[tr_index] = model; + int rf_index = rf->index(); + if (wire_slew_degradation_tbls_[rf_index]) + delete wire_slew_degradation_tbls_[rf_index]; + wire_slew_degradation_tbls_[rf_index] = model; } TableModel * @@ -335,36 +335,32 @@ LibertyLibrary::wireSlewDegradationTable(const RiseFall *rf) const } float -LibertyLibrary::degradeWireSlew(const LibertyCell *cell, - const RiseFall *rf, - const Pvt *pvt, +LibertyLibrary::degradeWireSlew(const RiseFall *rf, float in_slew, float wire_delay) const { const TableModel *model = wireSlewDegradationTable(rf); if (model) - return degradeWireSlew(cell, pvt, model, in_slew, wire_delay); + return degradeWireSlew(model, in_slew, wire_delay); else return in_slew; } float -LibertyLibrary::degradeWireSlew(const LibertyCell *cell, - const Pvt *pvt, - const TableModel *model, +LibertyLibrary::degradeWireSlew(const TableModel *model, float in_slew, float wire_delay) const { switch (model->order()) { case 0: - return model->findValue(this, cell, pvt, 0.0, 0.0, 0.0); + return model->findValue(0.0, 0.0, 0.0); case 1: { TableAxisPtr axis1 = model->axis1(); TableAxisVariable var1 = axis1->variable(); if (var1 == TableAxisVariable::output_pin_transition) - return model->findValue(this, cell, pvt, in_slew, 0.0, 0.0); + return model->findValue(in_slew, 0.0, 0.0); else if (var1 == TableAxisVariable::connect_delay) - return model->findValue(this, cell, pvt, wire_delay, 0.0, 0.0); + return model->findValue(wire_delay, 0.0, 0.0); else { criticalError(231, "unsupported slew degradation table axes"); return 0.0; @@ -377,10 +373,10 @@ LibertyLibrary::degradeWireSlew(const LibertyCell *cell, TableAxisVariable var2 = axis2->variable(); if (var1 == TableAxisVariable::output_pin_transition && var2 == TableAxisVariable::connect_delay) - return model->findValue(this, cell, pvt, in_slew, wire_delay, 0.0); + return model->findValue(in_slew, wire_delay, 0.0); else if (var1 == TableAxisVariable::connect_delay && var2 == TableAxisVariable::output_pin_transition) - return model->findValue(this, cell, pvt, wire_delay, in_slew, 0.0); + return model->findValue(wire_delay, in_slew, 0.0); else { criticalError(232, "unsupported slew degradation table axes"); return 0.0; @@ -2580,13 +2576,12 @@ LibertyPort::clockTreePathDelays() GateTimingModel *gate_model = dynamic_cast(model); ArcDelay delay; Slew slew; - gate_model->gateDelay(liberty_cell_, nullptr, 0.0, 0.0, 0.0, false, - delay, slew); + gate_model->gateDelay(nullptr, 0.0, 0.0, 0.0, false, delay, slew); const RiseFall *rf = arc->toEdge()->asRiseFall(); const MinMax *min_max = (role == TimingRole::clockTreePathMin()) ? MinMax::min() : MinMax::max(); - delays.setValue(rf, min_max, delay); + delays.setValue(rf, min_max, delayAsFloat(delay)); } } } @@ -2911,8 +2906,8 @@ ScaleFactors::ScaleFactors(const char *name) : { for (int type = 0; type < scale_factor_type_count; type++) { for (int pvt = 0; pvt < scale_factor_pvt_count; pvt++) { - for (auto tr_index : RiseFall::rangeIndex()) { - scales_[type][pvt][tr_index] = 0.0; + for (auto rf_index : RiseFall::rangeIndex()) { + scales_[type][pvt][rf_index] = 0.0; } } } @@ -2951,9 +2946,9 @@ ScaleFactors::scale(ScaleFactorType type, float ScaleFactors::scale(ScaleFactorType type, ScaleFactorPvt pvt, - int tr_index) + int rf_index) { - return scales_[int(type)][int(pvt)][tr_index]; + return scales_[int(type)][int(pvt)][rf_index]; } float @@ -3050,9 +3045,9 @@ OcvDerate::OcvDerate(const char *name) : name_(name) { for (auto el_index : EarlyLate::rangeIndex()) { - for (auto tr_index : RiseFall::rangeIndex()) { - derate_[tr_index][el_index][int(PathType::clk)] = nullptr; - derate_[tr_index][el_index][int(PathType::data)] = nullptr; + for (auto rf_index : RiseFall::rangeIndex()) { + derate_[rf_index][el_index][int(PathType::clk)] = nullptr; + derate_[rf_index][el_index][int(PathType::data)] = nullptr; } } } diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 8ec9ff1d..7cf3491d 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -1670,13 +1670,15 @@ LibertyReader::visitScaleFactor(LibertyAttr *attr) void LibertyReader::beginOpCond(LibertyGroup *group) { - const char *name = group->firstName(); - if (name) { - op_cond_ = new OperatingConditions(name); - library_->addOperatingConditions(op_cond_); + if (library_) { + const char *name = group->firstName(); + if (name) { + op_cond_ = new OperatingConditions(name); + library_->addOperatingConditions(op_cond_); + } + else + libWarn(68, group, "operating_conditions missing name."); } - else - libWarn(68, group, "operating_conditions missing name."); } void @@ -1863,9 +1865,11 @@ LibertyReader::beginCell(LibertyGroup *group) const char *name = group->firstName(); if (name) { debugPrint(debug_, "liberty", 1, "cell %s", name); - cell_ = builder_.makeCell(library_, name, filename_); - in_bus_ = false; - in_bundle_ = false; + if (library_) { + cell_ = builder_.makeCell(library_, name, filename_); + in_bus_ = false; + in_bundle_ = false; + } } else libWarn(78, group, "cell missing name."); @@ -1935,7 +1939,7 @@ void LibertyReader::makeTimingArcs(PortGroup *port_group) { for (TimingGroup *timing : port_group->timingGroups()) { - timing->makeTimingModels(library_, this); + timing->makeTimingModels(cell_, this); for (LibertyPort *port : *port_group->ports()) makeTimingArcs(port, timing); @@ -2200,15 +2204,15 @@ LibertyReader::makeTimingArcs(LibertyPort *to_port, } void -TimingGroup::makeTimingModels(LibertyLibrary *library, +TimingGroup::makeTimingModels(LibertyCell *cell, LibertyReader *visitor) { - switch (library->delayModelType()) { + switch (cell->libertyLibrary()->delayModelType()) { case DelayModelType::cmos_linear: - makeLinearModels(library); + makeLinearModels(cell); break; case DelayModelType::table: - makeTableModels(visitor); + makeTableModels(cell, visitor); break; case DelayModelType::cmos_pwl: case DelayModelType::cmos2: @@ -2219,8 +2223,9 @@ TimingGroup::makeTimingModels(LibertyLibrary *library, } void -TimingGroup::makeLinearModels(LibertyLibrary *library) +TimingGroup::makeLinearModels(LibertyCell *cell) { + LibertyLibrary *library = cell->libertyLibrary(); for (auto rf : RiseFall::range()) { int rf_index = rf->index(); float intr = intrinsic_[rf_index]; @@ -2230,7 +2235,7 @@ TimingGroup::makeLinearModels(LibertyLibrary *library) TimingModel *model = nullptr; if (timingTypeIsCheck(attrs_->timingType())) { if (intr_exists) - model = new CheckLinearModel(intr); + model = new CheckLinearModel(cell, intr); } else { float res = resistance_[rf_index]; @@ -2241,22 +2246,23 @@ TimingGroup::makeLinearModels(LibertyLibrary *library) if (!res_exists) res = 0.0F; if (intr_exists) - model = new GateLinearModel(intr, res); + model = new GateLinearModel(cell, intr, res); } attrs_->setModel(rf, model); } } void -TimingGroup::makeTableModels(LibertyReader *reader) +TimingGroup::makeTableModels(LibertyCell *cell, + LibertyReader *reader) { for (auto rf : RiseFall::range()) { int rf_index = rf->index(); - TableModel *cell = cell_[rf_index]; + TableModel *delay = cell_[rf_index]; TableModel *transition = transition_[rf_index]; TableModel *constraint = constraint_[rf_index]; - if (cell || transition) { - attrs_->setModel(rf, new GateTableModel(cell, delay_sigma_[rf_index], + if (delay || transition) { + attrs_->setModel(rf, new GateTableModel(cell, delay, delay_sigma_[rf_index], transition, slew_sigma_[rf_index], receiver_model_, @@ -2277,11 +2283,11 @@ TimingGroup::makeTableModels(LibertyReader *reader) || timing_type == TimingType::three_state_enable_rise) { if (transition == nullptr) reader->libWarn(95, line_, "missing %s_transition.", rf->name()); - if (cell == nullptr) + if (delay == nullptr) reader->libWarn(96, line_, "missing cell_%s.", rf->name()); } } else if (constraint) - attrs_->setModel(rf, new CheckTableModel(constraint, + attrs_->setModel(rf, new CheckTableModel(cell, constraint, constraint_sigma_[rf_index])); } } @@ -2520,7 +2526,7 @@ LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) slew_axis->findAxisIndex(waveform->slew(), slew_index, slew_exists); cap_axis->findAxisIndex(waveform->cap(), cap_index, cap_exists); if (slew_exists && cap_exists) { - size_t index = slew_index * slew_axis->size() + cap_index; + size_t index = slew_index * cap_axis->size() + cap_index; current_waveforms[index] = waveform->stealCurrents(); (*ref_times)[slew_index] = waveform->referenceTime(); } @@ -4196,7 +4202,7 @@ LibertyReader::beginTable(LibertyGroup *group, float scale) { const char *template_name = group->firstName(); - if (template_name) { + if (library_ && template_name) { tbl_template_ = library_->findTableTemplate(template_name, type); if (tbl_template_) { axis_[0] = tbl_template_->axis1(); @@ -4385,7 +4391,7 @@ LibertyReader::endLut(LibertyGroup *) void LibertyReader::beginTestCell(LibertyGroup *group) { - if (cell_->testCell()) + if (cell_ && cell_->testCell()) libWarn(169, group, "cell %s test_cell redefinition.", cell_->name()); else { test_cell_ = new TestCell; diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 4a32067d..f99fa1f1 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -784,7 +784,7 @@ public: TableModel *transition(RiseFall *rf); void setTransition(RiseFall *rf, TableModel *model); - void makeTimingModels(LibertyLibrary *library, + void makeTimingModels(LibertyCell *cell, LibertyReader *visitor); void setDelaySigma(RiseFall *rf, EarlyLate *early_late, @@ -801,8 +801,9 @@ public: OutputWaveforms *output_current); protected: - void makeLinearModels(LibertyLibrary *library); - void makeTableModels(LibertyReader *reader); + void makeLinearModels(LibertyCell *cell); + void makeTableModels(LibertyCell *cell, + LibertyReader *reader); TimingArcAttrsPtr attrs_; const char *related_output_port_name_; diff --git a/liberty/LinearModel.cc b/liberty/LinearModel.cc index 6baa5671..1ba8ff8b 100644 --- a/liberty/LinearModel.cc +++ b/liberty/LinearModel.cc @@ -21,16 +21,17 @@ namespace sta { -GateLinearModel::GateLinearModel(float intrinsic, +GateLinearModel::GateLinearModel(LibertyCell *cell, + float intrinsic, float resistance) : + GateTimingModel(cell), intrinsic_(intrinsic), resistance_(resistance) { } void -GateLinearModel::gateDelay(const LibertyCell *, - const Pvt *, +GateLinearModel::gateDelay(const Pvt *, float, float load_cap, float, @@ -44,15 +45,14 @@ GateLinearModel::gateDelay(const LibertyCell *, } string -GateLinearModel::reportGateDelay(const LibertyCell *cell, - const Pvt *, +GateLinearModel::reportGateDelay(const Pvt *, float, float load_cap, float, bool, int digits) const { - const LibertyLibrary *library = cell->libertyLibrary(); + const LibertyLibrary *library = cell_->libertyLibrary(); const Units *units = library->units(); const Unit *time_unit = units->timeUnit(); const Unit *res_unit = units->resistanceUnit(); @@ -70,8 +70,7 @@ GateLinearModel::reportGateDelay(const LibertyCell *cell, } float -GateLinearModel::driveResistance(const LibertyCell *, - const Pvt *) const +GateLinearModel::driveResistance(const Pvt *) const { return resistance_; } @@ -81,14 +80,15 @@ GateLinearModel::setIsScaled(bool) { } -CheckLinearModel::CheckLinearModel(float intrinsic) : +CheckLinearModel::CheckLinearModel(LibertyCell *cell, + float intrinsic) : + CheckTimingModel(cell), intrinsic_(intrinsic) { } void -CheckLinearModel::checkDelay(const LibertyCell *, - const Pvt *, +CheckLinearModel::checkDelay(const Pvt *, float, float, float, @@ -99,8 +99,7 @@ CheckLinearModel::checkDelay(const LibertyCell *, } string -CheckLinearModel::reportCheckDelay(const LibertyCell *cell, - const Pvt *, +CheckLinearModel::reportCheckDelay(const Pvt *, float, const char *, float, @@ -108,7 +107,7 @@ CheckLinearModel::reportCheckDelay(const LibertyCell *cell, bool, int digits) const { - const LibertyLibrary *library = cell->libertyLibrary(); + const LibertyLibrary *library = cell_->libertyLibrary(); const Units *units = library->units(); const Unit *time_unit = units->timeUnit(); string result = "Check = "; diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 57e9865a..8400bc2f 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -34,19 +34,26 @@ using std::make_shared; static void deleteSigmaModels(TableModel *models[EarlyLate::index_count]); static string -reportPvt(const LibertyLibrary *library, - const Pvt *pvt, +reportPvt(const LibertyCell *cell, + const Pvt *pvt, int digits); static void appendSpaces(string &result, int count); -GateTableModel::GateTableModel(TableModel *delay_model, +TimingModel::TimingModel(LibertyCell *cell) : + cell_(cell) +{ +} + +GateTableModel::GateTableModel(LibertyCell *cell, + TableModel *delay_model, TableModel *delay_sigma_models[EarlyLate::index_count], TableModel *slew_model, TableModel *slew_sigma_models[EarlyLate::index_count], ReceiverModelPtr receiver_model, OutputWaveforms *output_waveforms) : + GateTimingModel(cell), delay_model_(delay_model), slew_model_(slew_model), receiver_model_(receiver_model), @@ -94,8 +101,7 @@ GateTableModel::setIsScaled(bool is_scaled) } void -GateTableModel::gateDelay(const LibertyCell *cell, - const Pvt *pvt, +GateTableModel::gateDelay(const Pvt *pvt, float in_slew, float load_cap, float related_out_cap, @@ -104,30 +110,23 @@ GateTableModel::gateDelay(const LibertyCell *cell, ArcDelay &gate_delay, Slew &drvr_slew) const { - const LibertyLibrary *library = cell->libertyLibrary(); - float delay = findValue(library, cell, pvt, delay_model_, in_slew, - load_cap, related_out_cap); + float delay = findValue(pvt, delay_model_, in_slew, load_cap, related_out_cap); float sigma_early = 0.0; float sigma_late = 0.0; if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) - sigma_early = findValue(library, cell, pvt, - delay_sigma_models_[EarlyLate::earlyIndex()], + sigma_early = findValue(pvt, delay_sigma_models_[EarlyLate::earlyIndex()], in_slew, load_cap, related_out_cap); if (pocv_enabled && delay_sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(library, cell, pvt, - delay_sigma_models_[EarlyLate::lateIndex()], + sigma_late = findValue(pvt, delay_sigma_models_[EarlyLate::lateIndex()], in_slew, load_cap, related_out_cap); gate_delay = makeDelay(delay, sigma_early, sigma_late); - float slew = findValue(library, cell, pvt, slew_model_, in_slew, - load_cap, related_out_cap); + float slew = findValue(pvt, slew_model_, in_slew, load_cap, related_out_cap); if (pocv_enabled && slew_sigma_models_[EarlyLate::earlyIndex()]) - sigma_early = findValue(library, cell, pvt, - slew_sigma_models_[EarlyLate::earlyIndex()], + sigma_early = findValue(pvt, slew_sigma_models_[EarlyLate::earlyIndex()], in_slew, load_cap, related_out_cap); if (pocv_enabled && slew_sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(library, cell, pvt, - slew_sigma_models_[EarlyLate::lateIndex()], + sigma_late = findValue(pvt, slew_sigma_models_[EarlyLate::lateIndex()], in_slew, load_cap, related_out_cap); // Clip negative slews to zero. if (slew < 0.0) @@ -136,39 +135,36 @@ GateTableModel::gateDelay(const LibertyCell *cell, } string -GateTableModel::reportGateDelay(const LibertyCell *cell, - const Pvt *pvt, +GateTableModel::reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, float related_out_cap, bool pocv_enabled, int digits) const { - const LibertyLibrary *library = cell->libertyLibrary(); - string result = reportPvt(library, pvt, digits); - result += reportTableLookup("Delay", library, cell, pvt, delay_model_, in_slew, + string result = reportPvt(cell_, pvt, digits); + result += reportTableLookup("Delay", pvt, delay_model_, in_slew, load_cap, related_out_cap, digits); if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) - result += reportTableLookup("Delay sigma(early)", library, cell, pvt, + result += reportTableLookup("Delay sigma(early)", pvt, delay_sigma_models_[EarlyLate::earlyIndex()], in_slew, load_cap, related_out_cap, digits); if (pocv_enabled && delay_sigma_models_[EarlyLate::lateIndex()]) - result += reportTableLookup("Delay sigma(late)", library, cell, pvt, + result += reportTableLookup("Delay sigma(late)", pvt, delay_sigma_models_[EarlyLate::lateIndex()], in_slew, load_cap, related_out_cap, digits); result += '\n'; - result += reportTableLookup("Slew", library, cell, pvt, slew_model_, in_slew, + result += reportTableLookup("Slew", pvt, slew_model_, in_slew, load_cap, related_out_cap, digits); if (pocv_enabled && slew_sigma_models_[EarlyLate::earlyIndex()]) - result += reportTableLookup("Slew sigma(early)", library, cell, pvt, + result += reportTableLookup("Slew sigma(early)", pvt, slew_sigma_models_[EarlyLate::earlyIndex()], in_slew, load_cap, related_out_cap, digits); if (pocv_enabled && slew_sigma_models_[EarlyLate::lateIndex()]) - result += reportTableLookup("Slew sigma(late)", library, cell, pvt, + result += reportTableLookup("Slew sigma(late)", pvt, slew_sigma_models_[EarlyLate::lateIndex()], in_slew, load_cap, related_out_cap, digits); - float drvr_slew = findValue(library, cell, pvt, slew_model_, in_slew, - load_cap, related_out_cap); + float drvr_slew = findValue(pvt, slew_model_, in_slew, load_cap, related_out_cap); if (drvr_slew < 0.0) result += "Negative slew clipped to 0.0\n"; return result; @@ -176,8 +172,6 @@ GateTableModel::reportGateDelay(const LibertyCell *cell, string GateTableModel::reportTableLookup(const char *result_name, - const LibertyLibrary *library, - const LibertyCell *cell, const Pvt *pvt, const TableModel *model, float in_slew, @@ -189,17 +183,16 @@ GateTableModel::reportTableLookup(const char *result_name, float axis_value1, axis_value2, axis_value3; findAxisValues(model, in_slew, load_cap, related_out_cap, axis_value1, axis_value2, axis_value3); - return model->reportValue(result_name, library, cell, pvt, - axis_value1, nullptr, axis_value2, axis_value3, + const LibertyLibrary *library = cell_->libertyLibrary(); + return model->reportValue(result_name, cell_, pvt, axis_value1, nullptr, + axis_value2, axis_value3, library->units()->timeUnit(), digits); } return ""; } float -GateTableModel::findValue(const LibertyLibrary *library, - const LibertyCell *cell, - const Pvt *pvt, +GateTableModel::findValue(const Pvt *pvt, const TableModel *model, float in_slew, float load_cap, @@ -209,8 +202,7 @@ GateTableModel::findValue(const LibertyLibrary *library, float axis_value1, axis_value2, axis_value3; findAxisValues(model, in_slew, load_cap, related_out_cap, axis_value1, axis_value2, axis_value3); - return model->findValue(library, cell, pvt, - axis_value1, axis_value2, axis_value3); + return model->findValue(cell_, pvt, axis_value1, axis_value2, axis_value3); } else return 0.0; @@ -264,42 +256,36 @@ GateTableModel::findAxisValues(const TableModel *model, // Use slew/Cload for the highest Cload, which approximates output // admittance as the "drive". float -GateTableModel::driveResistance(const LibertyCell *cell, - const Pvt *pvt) const +GateTableModel::driveResistance(const Pvt *pvt) const { float slew, cap; - maxCapSlew(cell, 0.0, pvt, slew, cap); + maxCapSlew(0.0, pvt, slew, cap); return slew / cap; } void -GateTableModel::maxCapSlew(const LibertyCell *cell, - float in_slew, +GateTableModel::maxCapSlew(float in_slew, const Pvt *pvt, float &slew, float &cap) const { - const LibertyLibrary *library = cell->libertyLibrary(); TableAxisPtr axis1 = slew_model_->axis1(); TableAxisPtr axis2 = slew_model_->axis2(); TableAxisPtr axis3 = slew_model_->axis3(); if (axis1 && axis1->variable() == TableAxisVariable::total_output_net_capacitance) { cap = axis1->axisValue(axis1->size() - 1); - slew = findValue(library, cell, pvt, slew_model_, - in_slew, cap, 0.0); + slew = findValue(pvt, slew_model_, in_slew, cap, 0.0); } else if (axis2 && axis2->variable()==TableAxisVariable::total_output_net_capacitance) { cap = axis2->axisValue(axis2->size() - 1); - slew = findValue(library, cell, pvt, slew_model_, - in_slew, cap, 0.0); + slew = findValue(pvt, slew_model_, in_slew, cap, 0.0); } else if (axis3 && axis3->variable()==TableAxisVariable::total_output_net_capacitance) { cap = axis3->axisValue(axis3->size() - 1); - slew = findValue(library, cell, pvt, slew_model_, - in_slew, cap, 0.0); + slew = findValue(pvt, slew_model_, in_slew, cap, 0.0); } else { // Table not dependent on capacitance. @@ -399,8 +385,10 @@ ReceiverModel::checkAxes(TablePtr table) //////////////////////////////////////////////////////////////// -CheckTableModel::CheckTableModel(TableModel *model, +CheckTableModel::CheckTableModel(LibertyCell *cell, + TableModel *model, TableModel *sigma_models[EarlyLate::index_count]) : + CheckTimingModel(cell), model_(model) { for (auto el_index : EarlyLate::rangeIndex()) @@ -420,8 +408,7 @@ CheckTableModel::setIsScaled(bool is_scaled) } void -CheckTableModel::checkDelay(const LibertyCell *cell, - const Pvt *pvt, +CheckTableModel::checkDelay(const Pvt *pvt, float from_slew, float to_slew, float related_out_cap, @@ -430,18 +417,14 @@ CheckTableModel::checkDelay(const LibertyCell *cell, ArcDelay &margin) const { if (model_) { - const LibertyLibrary *library = cell->libertyLibrary(); - float mean = findValue(library, cell, pvt, model_, - from_slew, to_slew, related_out_cap); + float mean = findValue(pvt, model_, from_slew, to_slew, related_out_cap); float sigma_early = 0.0; float sigma_late = 0.0; if (pocv_enabled && sigma_models_[EarlyLate::earlyIndex()]) - sigma_early = findValue(library, cell, pvt, - sigma_models_[EarlyLate::earlyIndex()], + sigma_early = findValue(pvt, sigma_models_[EarlyLate::earlyIndex()], from_slew, to_slew, related_out_cap); if (pocv_enabled && sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(library, cell, pvt, - sigma_models_[EarlyLate::lateIndex()], + sigma_late = findValue(pvt, sigma_models_[EarlyLate::lateIndex()], from_slew, to_slew, related_out_cap); margin = makeDelay(mean, sigma_early, sigma_late); } @@ -450,9 +433,7 @@ CheckTableModel::checkDelay(const LibertyCell *cell, } float -CheckTableModel::findValue(const LibertyLibrary *library, - const LibertyCell *cell, - const Pvt *pvt, +CheckTableModel::findValue(const Pvt *pvt, const TableModel *model, float from_slew, float to_slew, @@ -462,16 +443,14 @@ CheckTableModel::findValue(const LibertyLibrary *library, float axis_value1, axis_value2, axis_value3; findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2, axis_value3); - return model->findValue(library, cell, pvt, - axis_value1, axis_value2, axis_value3); + return model->findValue(cell_, pvt, axis_value1, axis_value2, axis_value3); } else return 0.0; } string -CheckTableModel::reportCheckDelay(const LibertyCell *cell, - const Pvt *pvt, +CheckTableModel::reportCheckDelay(const Pvt *pvt, float from_slew, const char *from_slew_annotation, float to_slew, @@ -479,17 +458,16 @@ CheckTableModel::reportCheckDelay(const LibertyCell *cell, bool pocv_enabled, int digits) const { - const LibertyLibrary *library = cell->libertyLibrary(); - string result = reportTableDelay("Check", library, cell, pvt, model_, + string result = reportTableDelay("Check", pvt, model_, from_slew, from_slew_annotation, to_slew, related_out_cap, digits); if (pocv_enabled && sigma_models_[EarlyLate::earlyIndex()]) - result += reportTableDelay("Check sigma early", library, cell, pvt, + result += reportTableDelay("Check sigma early", pvt, sigma_models_[EarlyLate::earlyIndex()], from_slew, from_slew_annotation, to_slew, related_out_cap, digits); if (pocv_enabled && sigma_models_[EarlyLate::lateIndex()]) - result += reportTableDelay("Check sigma late", library, cell, pvt, + result += reportTableDelay("Check sigma late", pvt, sigma_models_[EarlyLate::lateIndex()], from_slew, from_slew_annotation, to_slew, related_out_cap, digits); @@ -498,8 +476,6 @@ CheckTableModel::reportCheckDelay(const LibertyCell *cell, string CheckTableModel::reportTableDelay(const char *result_name, - const LibertyLibrary *library, - const LibertyCell *cell, const Pvt *pvt, const TableModel *model, float from_slew, @@ -512,10 +488,11 @@ CheckTableModel::reportTableDelay(const char *result_name, float axis_value1, axis_value2, axis_value3; findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2, axis_value3); - string result = reportPvt(library, pvt, digits); - result += model_->reportValue(result_name, library, cell, pvt, + string result = reportPvt(cell_, pvt, digits); + result += model_->reportValue(result_name, cell_, pvt, axis_value1, from_slew_annotation, axis_value2, - axis_value3, library->units()->timeUnit(), digits); + axis_value3, + cell_->libertyLibrary()->units()->timeUnit(), digits); return result; } return ""; @@ -665,20 +642,26 @@ TableModel::value(size_t axis_index1, } float -TableModel::findValue(const LibertyLibrary *library, - const LibertyCell *cell, +TableModel::findValue(float axis_value1, + float axis_value2, + float axis_value3) const +{ + return table_->findValue(axis_value1, axis_value2, axis_value3); +} + +float +TableModel::findValue(const LibertyCell *cell, const Pvt *pvt, float axis_value1, float axis_value2, float axis_value3) const { return table_->findValue(axis_value1, axis_value2, axis_value3) - * scaleFactor(library, cell, pvt); + * scaleFactor(cell, pvt); } float -TableModel::scaleFactor(const LibertyLibrary *library, - const LibertyCell *cell, +TableModel::scaleFactor(const LibertyCell *cell, const Pvt *pvt) const { if (is_scaled_) @@ -686,13 +669,12 @@ TableModel::scaleFactor(const LibertyLibrary *library, // nominal pvt. return 1.0F; else - return library->scaleFactor(static_cast(scale_factor_type_), - rf_index_, cell, pvt); + return cell->libertyLibrary()->scaleFactor(static_cast(scale_factor_type_), + rf_index_, cell, pvt); } string TableModel::reportValue(const char *result_name, - const LibertyLibrary *library, const LibertyCell *cell, const Pvt *pvt, float value1, @@ -702,24 +684,24 @@ TableModel::reportValue(const char *result_name, const Unit *table_unit, int digits) const { - string result = table_->reportValue("Table value", library, cell, pvt, value1, + string result = table_->reportValue("Table value", cell, pvt, value1, comment1, value2, value3, table_unit, digits); - result += reportPvtScaleFactor(library, cell, pvt, digits); + result += reportPvtScaleFactor(cell, pvt, digits); result += result_name; result += " = "; - result += table_unit->asString(findValue(library, cell, pvt, - value1, value2, value3), digits); + result += table_unit->asString(findValue(cell, pvt, value1, value2, value3), digits); result += '\n'; return result; } static string -reportPvt(const LibertyLibrary *library, +reportPvt(const LibertyCell *cell, const Pvt *pvt, int digits) { + const LibertyLibrary *library = cell->libertyLibrary(); if (pvt == nullptr) pvt = library->defaultOperatingConditions(); if (pvt) { @@ -734,18 +716,17 @@ reportPvt(const LibertyLibrary *library, } string -TableModel::reportPvtScaleFactor(const LibertyLibrary *library, - const LibertyCell *cell, +TableModel::reportPvtScaleFactor(const LibertyCell *cell, const Pvt *pvt, int digits) const { if (pvt == nullptr) - pvt = library->defaultOperatingConditions(); + pvt = cell->libertyLibrary()->defaultOperatingConditions(); if (pvt) { string result; stringPrint(result, "PVT scale factor = %.*f\n", digits, - scaleFactor(library, cell, pvt)); + scaleFactor(cell, pvt)); return result; } return ""; @@ -777,7 +758,6 @@ Table0::findValue(float, string Table0::reportValue(const char *result_name, - const LibertyLibrary *, const LibertyCell *, const Pvt *, float value1, @@ -930,9 +910,8 @@ Table1::findValueClipZero(float axis_value1) const } string -Table1::reportValue(const char *result_name, const - LibertyLibrary *library, - const LibertyCell *, +Table1::reportValue(const char *result_name, + const LibertyCell *cell, const Pvt *, float value1, const char *comment1, @@ -941,7 +920,7 @@ Table1::reportValue(const char *result_name, const const Unit *table_unit, int digits) const { - const Units *units = library->units(); + const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); string result = "Table is indexed by\n "; result += axis1_->variableString(); @@ -1098,8 +1077,7 @@ Table2::findValue(float axis_value1, string Table2::reportValue(const char *result_name, - const LibertyLibrary *library, - const LibertyCell *, + const LibertyCell *cell, const Pvt *, float value1, const char *comment1, @@ -1108,7 +1086,7 @@ Table2::reportValue(const char *result_name, const Unit *table_unit, int digits) const { - const Units *units = library->units(); + const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); const Unit *unit2 = axis2_->unit(units); string result = "------- "; @@ -1289,8 +1267,7 @@ Table3::findValue(float axis_value1, // 0.40 | 0.20 0.30 string Table3::reportValue(const char *result_name, - const LibertyLibrary *library, - const LibertyCell *, + const LibertyCell *cell, const Pvt *, float value1, const char *comment1, @@ -1299,7 +1276,7 @@ Table3::reportValue(const char *result_name, const Unit *table_unit, int digits) const { - const Units *units = library->units(); + const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); const Unit *unit2 = axis2_->unit(units); const Unit *unit3 = axis3_->unit(units); @@ -1662,11 +1639,11 @@ OutputWaveforms::timeCurrent(float slew, { size_t slew_index = slew_axis_->findAxisIndex(slew); size_t cap_index = cap_axis_->findAxisIndex(cap); - size_t slew_count = slew_axis_->size(); - size_t wave_index00 = slew_index * slew_count + cap_index; - size_t wave_index01 = slew_index * slew_count + (cap_index + 1); - size_t wave_index10 = (slew_index + 1) * slew_count + cap_index; - size_t wave_index11 = (slew_index + 1) * slew_count + (cap_index + 1); + size_t cap_count = cap_axis_->size(); + size_t wave_index00 = slew_index * cap_count + cap_index; + size_t wave_index01 = slew_index * cap_count + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * cap_count + cap_index; + size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1); const Table1 *waveform00 = current_waveforms_[wave_index00]; const Table1 *waveform01 = current_waveforms_[wave_index01]; @@ -1733,11 +1710,11 @@ OutputWaveforms::voltageTime(float slew, { size_t slew_index = slew_axis_->findAxisIndex(slew); size_t cap_index = cap_axis_->findAxisIndex(cap); - size_t slew_count = slew_axis_->size(); - size_t wave_index00 = slew_index * slew_count + cap_index; - size_t wave_index01 = slew_index * slew_count + (cap_index + 1); - size_t wave_index10 = (slew_index + 1) * slew_count + cap_index; - size_t wave_index11 = (slew_index + 1) * slew_count + (cap_index + 1); + size_t cap_count = cap_axis_->size(); + size_t wave_index00 = slew_index * cap_count + cap_index; + size_t wave_index01 = slew_index * cap_count + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * cap_count + cap_index; + size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1); float cap0 = cap_axis_->axisValue(cap_index); float cap1 = cap_axis_->axisValue(cap_index + 1); @@ -1858,11 +1835,11 @@ OutputWaveforms::voltageCurrent(float slew, { size_t slew_index = slew_axis_->findAxisIndex(slew); size_t cap_index = cap_axis_->findAxisIndex(cap); - size_t slew_count = slew_axis_->size(); - size_t wave_index00 = slew_index * slew_count + cap_index; - size_t wave_index01 = slew_index * slew_count + (cap_index + 1); - size_t wave_index10 = (slew_index + 1) * slew_count + cap_index; - size_t wave_index11 = (slew_index + 1) * slew_count + (cap_index + 1); + size_t cap_count = cap_axis_->size(); + size_t wave_index00 = slew_index * cap_count + cap_index; + size_t wave_index01 = slew_index * cap_count + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * cap_count + cap_index; + size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1); float cap0 = cap_axis_->axisValue(cap_index); float cap1 = cap_axis_->axisValue(cap_index + 1); diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index ba7301c7..0f1532e3 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -149,10 +149,8 @@ float TimingArc::driveResistance() const { GateTimingModel *model = dynamic_cast(model_); - if (model) { - LibertyCell *cell = set_->libertyCell(); - return model->driveResistance(cell, nullptr); - } + if (model) + return model->driveResistance(nullptr); else return 0.0; } @@ -162,11 +160,9 @@ TimingArc::intrinsicDelay() const { GateTimingModel *model = dynamic_cast(model_); if (model) { - LibertyCell *cell = set_->libertyCell(); ArcDelay arc_delay; Slew slew; - model->gateDelay(cell, nullptr, 0.0, 0.0, 0.0, false, - arc_delay, slew); + model->gateDelay(nullptr, 0.0, 0.0, 0.0, false, arc_delay, slew); return arc_delay; } else diff --git a/liberty/TimingModel.cc b/liberty/TimingModel.cc new file mode 100644 index 00000000..efb73cf6 --- /dev/null +++ b/liberty/TimingModel.cc @@ -0,0 +1,31 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2023, Parallax Software, Inc. +// +// 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 . + +#include "TimingModel.hh" + +namespace sta { + +GateTimingModel::GateTimingModel(LibertyCell *cell) : + TimingModel(cell) +{ +} + +CheckTimingModel::CheckTimingModel(LibertyCell *cell) : + TimingModel(cell) +{ +} + +} // namespace diff --git a/liberty/TimingRole.cc b/liberty/TimingRole.cc index 4747a2e6..ae37ce8c 100644 --- a/liberty/TimingRole.cc +++ b/liberty/TimingRole.cc @@ -176,6 +176,11 @@ TimingRole::destroy() non_seq_setup_ = nullptr; delete non_seq_hold_; non_seq_hold_ = nullptr; + delete clock_tree_path_min_; + clock_tree_path_min_ = nullptr; + delete clock_tree_path_max_; + clock_tree_path_max_ = nullptr; + timing_roles_.clear(); } diff --git a/power/Power.cc b/power/Power.cc index 2a516f17..5350a331 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -17,7 +17,7 @@ #include "Power.hh" #include // max -#include // aps +#include // abs #include "Debug.hh" #include "EnumNameMap.hh" @@ -47,6 +47,13 @@ #include "Bfs.hh" #include "ClkNetwork.hh" +#if CUDD +#include "cudd.h" +#else +#define Cudd_Init(ignore1, ignore2, ignore3, ignore4, ignore5) nullptr +#define Cudd_Quit(ignore1) +#endif + // Related liberty not supported: // library // default_cell_leakage_power : 0; @@ -71,15 +78,32 @@ isPositiveUnate(const LibertyCell *cell, const LibertyPort *from, const LibertyPort *to); +static EnumNameMap pwr_activity_origin_map = + {{PwrActivityOrigin::global, "global"}, + {PwrActivityOrigin::input, "input"}, + {PwrActivityOrigin::user, "user"}, + {PwrActivityOrigin::vcd, "vcd"}, + {PwrActivityOrigin::propagated, "propagated"}, + {PwrActivityOrigin::clock, "clock"}, + {PwrActivityOrigin::constant, "constant"}, + {PwrActivityOrigin::defaulted, "defaulted"}, + {PwrActivityOrigin::unknown, "unknown"}}; + Power::Power(StaState *sta) : StaState(sta), global_activity_{0.0, 0.0, PwrActivityOrigin::unknown}, input_activity_{0.1, 0.5, PwrActivityOrigin::input}, seq_activity_map_(100, SeqPinHash(network_), SeqPinEqual()), - activities_valid_(false) + activities_valid_(false), + cudd_mgr_(Cudd_Init(0, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0)) { } +Power::~Power() +{ + Cudd_Quit(cudd_mgr_); +} + void Power::setGlobalActivity(float activity, float duty) @@ -111,9 +135,9 @@ Power::setInputPortActivity(const Port *input_port, void Power::setUserActivity(const Pin *pin, - float activity, - float duty, - PwrActivityOrigin origin) + float activity, + float duty, + PwrActivityOrigin origin) { user_activity_map_[pin] = {activity, duty, origin}; activities_valid_ = false; @@ -135,6 +159,11 @@ void Power::setActivity(const Pin *pin, PwrActivity &activity) { + debugPrint(debug_, "power_activity", 3, "set %s %.2e %.2f %s", + network_->pathName(pin), + activity.activity(), + activity.duty(), + pwr_activity_origin_map.find(activity.origin())); activity_map_[pin] = activity; } @@ -168,7 +197,7 @@ Power::hasSeqActivity(const Instance *reg, return seq_activity_map_.hasKey(SeqPin(reg, output)); } -PwrActivity +PwrActivity & Power::seqActivity(const Instance *reg, LibertyPort *output) { @@ -331,7 +360,7 @@ private: bool setActivityCheck(const Pin *pin, PwrActivity &activity); - static constexpr float change_tolerance_ = .01; + static constexpr float change_tolerance_ = .001; InstanceSet visited_regs_; float max_change_; Power *power_; @@ -370,10 +399,6 @@ PropActivityVisitor::visit(Vertex *vertex) bool changed = false; if (power_->hasUserActivity(pin)) { PwrActivity &activity = power_->userActivity(pin); - debugPrint(debug_, "power_activity", 3, "set %s %.2e %.2f", - vertex->name(network_), - activity.activity(), - activity.duty()); changed = setActivityCheck(pin, activity); } else { @@ -397,12 +422,8 @@ PropActivityVisitor::visit(Vertex *vertex) if (port) { FuncExpr *func = port->function(); if (func) { - PwrActivity activity = power_->evalActivity(func, inst); + PwrActivity activity = power_->evalActivity(func, inst); changed = setActivityCheck(pin, activity); - debugPrint(debug_, "power_activity", 3, "set %s %.2e %.2f", - vertex->name(network_), - activity.activity(), - activity.duty()); } if (port->isClockGateOut()) { const Pin *enable, *clk, *gclk; @@ -491,6 +512,210 @@ Power::clockGatePins(const Instance *inst, delete pin_iter; } +//////////////////////////////////////////////////////////////// + +#if CUDD + +PwrActivity +Power::evalActivity(FuncExpr *expr, + const Instance *inst) +{ + LibertyPort *func_port = expr->port(); + if (func_port && func_port->direction()->isInternal()) + return findSeqActivity(inst, func_port); + else { + DdNode *bdd = funcBdd(expr); + float duty = evalBddDuty(bdd, inst); + float activity = evalBddActivity(bdd, inst); + + Cudd_RecursiveDeref(cudd_mgr_, bdd); + clearVarMap(); + return PwrActivity(activity, duty, PwrActivityOrigin::propagated); + } +} + +// Find duty when from_port is sensitized. +float +Power::evalDiffDuty(FuncExpr *expr, + LibertyPort *from_port, + const Instance *inst) +{ + DdNode *bdd = funcBdd(expr); + DdNode *var_node = bdd_port_var_map_[from_port]; + unsigned var_index = Cudd_NodeReadIndex(var_node); + DdNode *diff = Cudd_bddBooleanDiff(cudd_mgr_, bdd, var_index); + Cudd_Ref(diff); + float duty = evalBddDuty(diff, inst); + + Cudd_RecursiveDeref(cudd_mgr_, diff); + Cudd_RecursiveDeref(cudd_mgr_, bdd); + clearVarMap(); + return duty; +} + +DdNode * +Power::funcBdd(const FuncExpr *expr) +{ + DdNode *left = nullptr; + DdNode *right = nullptr; + DdNode *result = nullptr; + switch (expr->op()) { + case FuncExpr::op_port: { + LibertyPort *port = expr->port(); + result = ensureNode(port); + break; + } + case FuncExpr::op_not: + left = funcBdd(expr->left()); + if (left) + result = Cudd_Not(left); + break; + case FuncExpr::op_or: + left = funcBdd(expr->left()); + right = funcBdd(expr->right()); + if (left && right) + result = Cudd_bddOr(cudd_mgr_, left, right); + else if (left) + result = left; + else if (right) + result = right; + break; + case FuncExpr::op_and: + left = funcBdd(expr->left()); + right = funcBdd(expr->right()); + if (left && right) + result = Cudd_bddAnd(cudd_mgr_, left, right); + else if (left) + result = left; + else if (right) + result = right; + break; + case FuncExpr::op_xor: + left = funcBdd(expr->left()); + right = funcBdd(expr->right()); + if (left && right) + result = Cudd_bddXor(cudd_mgr_, left, right); + else if (left) + result = left; + else if (right) + result = right; + break; + case FuncExpr::op_one: + result = Cudd_ReadOne(cudd_mgr_); + break; + case FuncExpr::op_zero: + result = Cudd_ReadLogicZero(cudd_mgr_); + break; + default: + report_->critical(596, "unknown function operator"); + } + if (result) + Cudd_Ref(result); + if (left) + Cudd_RecursiveDeref(cudd_mgr_, left); + if (right) + Cudd_RecursiveDeref(cudd_mgr_, right); + return result; +} + +DdNode * +Power::ensureNode(LibertyPort *port) +{ + DdNode *bdd = bdd_port_var_map_.findKey(port); + if (bdd == nullptr) { + bdd = Cudd_bddNewVar(cudd_mgr_); + bdd_port_var_map_[port] = bdd; + unsigned var_index = Cudd_NodeReadIndex(bdd); + bdd_var_idx_port_map_[var_index] = port; + Cudd_Ref(bdd); + debugPrint(debug_, "power_activity", 2, "%s var %d", port->name(), var_index); + } + return bdd; +} + +void +Power::clearVarMap() +{ + for (auto port_node : bdd_port_var_map_) { + DdNode *var_node = port_node.second; + Cudd_RecursiveDeref(cudd_mgr_, var_node); + } + bdd_port_var_map_.clear(); + bdd_var_idx_port_map_.clear(); +} + +// As suggested by +// https://stackoverflow.com/questions/63326728/cudd-printminterm-accessing-the-individual-minterms-in-the-sum-of-products +float +Power::evalBddDuty(DdNode *bdd, + const Instance *inst) +{ + if (Cudd_IsConstant(bdd)) { + if (bdd == Cudd_ReadOne(cudd_mgr_)) + return 1.0; + else if (bdd == Cudd_ReadLogicZero(cudd_mgr_)) + return 0.0; + else + criticalError(1100, "unknown cudd constant"); + } + else { + float duty0 = evalBddDuty(Cudd_E(bdd), inst); + float duty1 = evalBddDuty(Cudd_T(bdd), inst); + unsigned int index = Cudd_NodeReadIndex(bdd); + int var_index = Cudd_ReadPerm(cudd_mgr_, index); + LibertyPort *port = bdd_var_idx_port_map_[var_index]; + if (port->direction()->isInternal()) + return findSeqActivity(inst, port).duty(); + else { + const Pin *pin = findLinkPin(inst, port); + if (pin) { + PwrActivity var_activity = findActivity(pin); + float var_duty = var_activity.duty(); + float duty = duty0 * (1.0 - var_duty) + duty1 * var_duty; + if (Cudd_IsComplement(bdd)) + duty = 1.0 - duty; + return duty; + } + } + } + return 0.0; +} + +// https://www.brown.edu/Departments/Engineering/Courses/engn2912/Lectures/LP-02-logic-power-est.pdf +// F(x0, x1, .. ) is sensitized when F(Xi=1) xor F(Xi=0) +// F(Xi=1), F(Xi=0) are the cofactors of F wrt Xi. +float +Power::evalBddActivity(DdNode *bdd, + const Instance *inst) +{ + float activity = 0.0; + for (auto port_var : bdd_port_var_map_) { + LibertyPort *port = port_var.first; + const Pin *pin = findLinkPin(inst, port); + if (pin) { + PwrActivity var_activity = findActivity(pin); + DdNode *var_node = port_var.second; + unsigned int var_index = Cudd_NodeReadIndex(var_node); + DdNode *diff = Cudd_bddBooleanDiff(cudd_mgr_, bdd, var_index); + Cudd_Ref(diff); + float diff_duty = evalBddDuty(diff, inst); + Cudd_RecursiveDeref(cudd_mgr_, diff); + float var_act = var_activity.activity() * diff_duty; + activity += var_act; + const Clock *clk = findClk(pin); + float clk_period = clk ? clk->period() : 1.0; + debugPrint(debug_, "power_activity", 3, "var %s %.3e * %.3f = %.3e", + port->name(), + var_activity.activity() / clk_period, + diff_duty, + var_act / clk_period); + } + } + return activity; +} + +#else + PwrActivity Power::evalActivity(FuncExpr *expr, const Instance *inst) @@ -519,8 +744,11 @@ Power::evalActivity(FuncExpr *expr, return findSeqActivity(inst, port); else { Pin *pin = findLinkPin(inst, port); - if (pin) - return findActivity(pin); + if (pin) { + PwrActivity activity = findActivity(pin); + activity.setOrigin(PwrActivityOrigin::propagated); + return activity; + } } return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant); } @@ -539,7 +767,8 @@ Power::evalActivity(FuncExpr *expr, float p1 = 1.0 - activity1.duty(); float p2 = 1.0 - activity2.duty(); return PwrActivity(activity1.activity() * p2 + activity2.activity() * p1, - 1.0 - p1 * p2, + // d1 + d2 - d1 * d2 + 1.0 - p1 * p2, PwrActivityOrigin::propagated); } case FuncExpr::op_and: { @@ -574,6 +803,23 @@ Power::evalActivity(FuncExpr *expr, return PwrActivity(); } +// Eval activity of difference(expr) wrt cofactor port. +float +Power::evalDiffDuty(FuncExpr *expr, + LibertyPort *cofactor_port, + const Instance *inst) +{ + // 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 p1 + p2; +} + +#endif // CUDD + //////////////////////////////////////////////////////////////// void @@ -684,6 +930,7 @@ Power::seedRegOutputActivities(const Instance *reg, activity.setActivity(1.0); if (invert) activity.setDuty(1.0 - activity.duty()); + activity.setOrigin(PwrActivityOrigin::propagated); setSeqActivity(reg, output, activity); } } @@ -696,29 +943,9 @@ Power::power(const Instance *inst, const Corner *corner) { PowerResult result; - MinMax *mm = MinMax::max(); - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); const Clock *inst_clk = findInstClk(inst); - 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); - if (to_port) { - float load_cap = to_port->direction()->isAnyOutput() - ? graph_delay_calc_->loadCap(to_pin, dcalc_ap) - : 0.0; - PwrActivity activity = findClkedActivity(to_pin, inst_clk); - if (to_port->direction()->isAnyOutput()) { - findSwitchingPower(cell, to_port, activity, load_cap, corner, result); - findOutputInternalPower(to_pin, to_port, inst, cell, activity, - load_cap, corner, result); - } - if (to_port->direction()->isAnyInput()) - findInputInternalPower(to_pin, to_port, inst, cell, activity, - load_cap, corner, result); - } - } - delete pin_iter; + findInternalPower(inst, cell, corner, inst_clk, result); + findSwitchingPower(inst, cell, corner, inst_clk, result); findLeakagePower(inst, cell, corner, result); return result; } @@ -731,16 +958,47 @@ Power::findInstClk(const Instance *inst) while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); const Clock *clk = findClk(pin); - if (clk) + if (clk) { inst_clk = clk; + break; + } } delete pin_iter; return inst_clk; } +void +Power::findInternalPower(const Instance *inst, + LibertyCell *cell, + const Corner *corner, + const Clock *inst_clk, + // Return values. + PowerResult &result) +{ + const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + const Pin *to_pin = pin_iter->next(); + LibertyPort *to_port = network_->libertyPort(to_pin); + if (to_port) { + float load_cap = to_port->direction()->isAnyOutput() + ? graph_delay_calc_->loadCap(to_pin, dcalc_ap) + : 0.0; + PwrActivity activity = findClkedActivity(to_pin, inst_clk); + if (to_port->direction()->isAnyOutput()) + findOutputInternalPower(to_port, inst, cell, activity, + load_cap, corner, result); + if (to_port->direction()->isAnyInput()) + findInputInternalPower(to_pin, to_port, inst, cell, activity, + load_cap, corner, result); + } + } + delete pin_iter; +} + void Power::findInputInternalPower(const Pin *pin, - const LibertyPort *port, + LibertyPort *port, const Instance *inst, LibertyCell *cell, PwrActivity &activity, @@ -755,16 +1013,14 @@ Power::findInputInternalPower(const Pin *pin, if (corner_cell && corner_port) { const InternalPowerSeq &internal_pwrs = corner_cell->internalPowers(corner_port); if (!internal_pwrs.empty()) { - debugPrint(debug_, "power", 2, "internal input %s/%s (%s)", + debugPrint(debug_, "power", 2, "internal input %s/%s cap %s", network_->pathName(inst), port->name(), - corner_cell->name()); + units_->capacitanceUnit()->asString(load_cap)); + debugPrint(debug_, "power", 2, " when act/ns duty energy power"); const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); const Pvt *pvt = dcalc_ap->operatingConditions(); Vertex *vertex = graph_->pinLoadVertex(pin); - debugPrint(debug_, "power", 2, " cap = %s", - units_->capacitanceUnit()->asString(load_cap)); - debugPrint(debug_, "power", 2, " when act/ns duty energy power"); float internal = 0.0; for (InternalPower *pwr : internal_pwrs) { const char *related_pg_pin = pwr->relatedPgPin(); @@ -783,13 +1039,13 @@ Power::findInputInternalPower(const Pin *pin, float duty = 1.0; // fallback default FuncExpr *when = pwr->when(); if (when) { - LibertyPort *out_corner_port = findExprOutPort(when); + const LibertyPort *out_corner_port = findExprOutPort(when); if (out_corner_port) { - const LibertyPort *out_port = findLinkPort(cell, out_corner_port); + LibertyPort *out_port = findLinkPort(cell, out_corner_port); if (out_port) { FuncExpr *func = out_port->function(); if (func && func->hasPort(port)) - duty = evalActivityDifference(func, inst, port).duty(); + duty = evalDiffDuty(func, port, inst); else duty = evalActivity(when, inst).duty(); } @@ -858,26 +1114,8 @@ Power::findExprOutPort(FuncExpr *expr) return nullptr; } -// 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); -} - void -Power::findOutputInternalPower(const Pin *to_pin, - const LibertyPort *to_port, +Power::findOutputInternalPower(const LibertyPort *to_port, const Instance *inst, LibertyCell *cell, PwrActivity &to_activity, @@ -886,45 +1124,48 @@ Power::findOutputInternalPower(const Pin *to_pin, // Return values. PowerResult &result) { - debugPrint(debug_, "power", 2, "internal output %s/%s (%s)", + debugPrint(debug_, "power", 2, "internal output %s/%s cap %s", network_->pathName(inst), to_port->name(), - cell->name()); + units_->capacitanceUnit()->asString(load_cap)); const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); const Pvt *pvt = dcalc_ap->operatingConditions(); LibertyCell *corner_cell = cell->cornerCell(dcalc_ap); const LibertyPort *to_corner_port = to_port->cornerPort(dcalc_ap); - debugPrint(debug_, "power", 2, " cap = %s", - units_->capacitanceUnit()->asString(load_cap)); FuncExpr *func = to_port->function(); map pg_duty_sum; for (InternalPower *pwr : corner_cell->internalPowers(to_corner_port)) { - float duty = findInputDuty(to_pin, inst, func, pwr); - const char *related_pg_pin = pwr->relatedPgPin(); - // Note related_pg_pin may be null. - pg_duty_sum[related_pg_pin] += duty; + const LibertyPort *from_corner_port = pwr->relatedPort(); + if (from_corner_port) { + const Pin *from_pin = findLinkPin(inst, from_corner_port); + float from_activity = findActivity(from_pin).activity(); + float duty = findInputDuty(inst, func, pwr); + const char *related_pg_pin = pwr->relatedPgPin(); + // Note related_pg_pin may be null. + pg_duty_sum[related_pg_pin] += from_activity * duty; + } } + debugPrint(debug_, "power", 2, + " when act/ns duty wgt energy power"); float internal = 0.0; for (InternalPower *pwr : corner_cell->internalPowers(to_corner_port)) { FuncExpr *when = pwr->when(); const char *related_pg_pin = pwr->relatedPgPin(); - float duty = findInputDuty(to_pin, inst, func, pwr); + float duty = findInputDuty(inst, func, pwr); Vertex *from_vertex = nullptr; bool positive_unate = true; const LibertyPort *from_corner_port = pwr->relatedPort(); + const Pin *from_pin = nullptr; if (from_corner_port) { positive_unate = isPositiveUnate(corner_cell, from_corner_port, to_corner_port); - const Pin *from_pin = findLinkPin(inst, from_corner_port); - if (from_pin) { + from_pin = findLinkPin(inst, from_corner_port); + if (from_pin) from_vertex = graph_->pinLoadVertex(from_pin); - } } float energy = 0.0; int rf_count = 0; - debugPrint(debug_, "power", 2, - " when act/ns duty wgt energy power"); for (RiseFall *to_rf : RiseFall::range()) { // Use unateness to find from_rf. RiseFall *from_rf = positive_unate ? to_rf : to_rf->opposite(); @@ -943,11 +1184,13 @@ Power::findOutputInternalPower(const Pin *to_pin, float weight = 0.0; if (duty_sum_iter != pg_duty_sum.end()) { float duty_sum = duty_sum_iter->second; - if (duty_sum != 0.0) - weight = duty / duty_sum; + if (duty_sum != 0.0 && from_pin) { + float from_activity = findActivity(from_pin).activity(); + weight = from_activity * duty / duty_sum; + } } float port_internal = weight * energy * to_activity.activity(); - debugPrint(debug_, "power", 2, "%3s -> %-3s %6s %.2f %.2f %.2f %9.2e %9.2e %s", + debugPrint(debug_, "power", 2, "%3s -> %-3s %6s %.3f %.3f %.3f %9.2e %9.2e %s", from_corner_port ? from_corner_port->name() : "-" , to_port->name(), when ? when->asString() : "", @@ -963,33 +1206,21 @@ Power::findOutputInternalPower(const Pin *to_pin, } float -Power::findInputDuty(const Pin *to_pin, - const Instance *inst, - FuncExpr *func, - InternalPower *pwr) +Power::findInputDuty(const Instance *inst, + FuncExpr *func, + InternalPower *pwr) { const LibertyPort *from_corner_port = pwr->relatedPort(); if (from_corner_port) { - const LibertyPort *from_port = findLinkPort(network_->libertyCell(inst), - from_corner_port); + LibertyPort *from_port = findLinkPort(network_->libertyCell(inst), + from_corner_port); const Pin *from_pin = network_->findPin(inst, from_port); if (from_pin) { FuncExpr *when = pwr->when(); Vertex *from_vertex = graph_->pinLoadVertex(from_pin); if (func && func->hasPort(from_port)) { - float from_activity = findActivity(from_pin).activity(); - float to_activity = findActivity(to_pin).activity(); - float duty1 = evalActivityDifference(func, inst, from_port).duty(); - 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; - } + float duty = evalDiffDuty(func, from_port, inst); return duty; } else if (when) @@ -1036,6 +1267,44 @@ isPositiveUnate(const LibertyCell *cell, //////////////////////////////////////////////////////////////// +void +Power::findSwitchingPower(const Instance *inst, + LibertyCell *cell, + const Corner *corner, + const Clock *inst_clk, + // Return values. + PowerResult &result) +{ + const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); + LibertyCell *corner_cell = cell->cornerCell(dcalc_ap); + 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); + if (to_port) { + float load_cap = to_port->direction()->isAnyOutput() + ? graph_delay_calc_->loadCap(to_pin, dcalc_ap) + : 0.0; + PwrActivity activity = findClkedActivity(to_pin, inst_clk); + if (to_port->direction()->isAnyOutput()) { + float volt = portVoltage(corner_cell, to_port, dcalc_ap); + float switching = .5 * load_cap * volt * volt * activity.activity(); + debugPrint(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e", + cell->name(), + to_port->name(), + activity.activity(), + volt, + switching); + result.switching() += switching; + } + } + } + delete pin_iter; +} + +//////////////////////////////////////////////////////////////// + + void Power::findLeakagePower(const Instance *inst, LibertyCell *cell, @@ -1097,29 +1366,6 @@ Power::findLeakagePower(const Instance *inst, result.leakage() += leakage; } -void -Power::findSwitchingPower(LibertyCell *cell, - const LibertyPort *to_port, - PwrActivity &activity, - float load_cap, - const Corner *corner, - // Return values. - PowerResult &result) -{ - MinMax *mm = MinMax::max(); - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); - LibertyCell *corner_cell = cell->cornerCell(dcalc_ap); - float volt = portVoltage(corner_cell, to_port, dcalc_ap); - float switching = .5 * load_cap * volt * volt * activity.activity(); - debugPrint(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e", - cell->name(), - to_port->name(), - activity.activity(), - volt, - switching); - result.switching() += switching; -} - PwrActivity Power::findClkedActivity(const Pin *pin) { @@ -1159,7 +1405,9 @@ Power::findActivity(const Pin *pin) if (activity.origin() != PwrActivityOrigin::unknown) return activity; } - return PwrActivity(2.0, 0.5, PwrActivityOrigin::clock); + const Clock *clk = findClk(pin); + float duty = clockDuty(clk); + return PwrActivity(2.0, duty, PwrActivityOrigin::clock); } else if (global_activity_.isSet()) return global_activity_; @@ -1171,6 +1419,25 @@ Power::findActivity(const Pin *pin) return PwrActivity(0.0, 0.0, PwrActivityOrigin::unknown); } +float +Power::clockDuty(const Clock *clk) +{ + if (clk->isGenerated()) { + const Clock *master = clk->masterClk(); + if (master == nullptr) + return 0.5; // punt + else + return clockDuty(master); + } + else { + const FloatSeq *waveform = clk->waveform(); + float rise_time = (*waveform)[0]; + float fall_time = (*waveform)[1]; + float duty = (fall_time - rise_time) / clk->period(); + return duty; + } +} + PwrActivity Power::findSeqActivity(const Instance *inst, LibertyPort *port) @@ -1178,11 +1445,11 @@ Power::findSeqActivity(const Instance *inst, if (global_activity_.isSet()) return global_activity_; else if (hasSeqActivity(inst, port)) { - PwrActivity activity = seqActivity(inst, port); + PwrActivity &activity = seqActivity(inst, port); if (activity.origin() != PwrActivityOrigin::unknown) return activity; } - return input_activity_; + return PwrActivity(0.0, 0.0, PwrActivityOrigin::unknown); } float @@ -1272,16 +1539,6 @@ PowerResult::incr(PowerResult &result) //////////////////////////////////////////////////////////////// -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) : @@ -1311,6 +1568,12 @@ PwrActivity::setDuty(float duty) duty_ = duty; } +void +PwrActivity::setOrigin(PwrActivityOrigin origin) +{ + origin_ = origin; +} + void PwrActivity::set(float activity, float duty, diff --git a/power/Power.hh b/power/Power.hh index 0359d486..f3075fb8 100644 --- a/power/Power.hh +++ b/power/Power.hh @@ -18,12 +18,16 @@ #include +#include "StaConfig.hh" // CUDD #include "UnorderedMap.hh" #include "Network.hh" #include "SdcClass.hh" #include "PowerClass.hh" #include "StaState.hh" +struct DdNode; +struct DdManager; + namespace sta { class Sta; @@ -34,6 +38,8 @@ class BfsFwdIterator; class Vertex; typedef std::pair SeqPin; +typedef Map BddPortVarMap; +typedef Map BddVarIdxPortMap; class SeqPinHash { @@ -62,6 +68,7 @@ class Power : public StaState { public: Power(StaState *sta); + ~Power(); void power(const Corner *corner, // Return values. PowerResult &total, @@ -100,8 +107,8 @@ protected: PwrActivity &activity); bool hasSeqActivity(const Instance *reg, LibertyPort *output); - PwrActivity seqActivity(const Instance *reg, - LibertyPort *output); + PwrActivity &seqActivity(const Instance *reg, + LibertyPort *output); bool hasActivity(const Pin *pin); void setActivity(const Pin *pin, PwrActivity &activity); @@ -109,8 +116,14 @@ protected: PowerResult power(const Instance *inst, LibertyCell *cell, const Corner *corner); + void findInternalPower(const Instance *inst, + LibertyCell *cell, + const Corner *corner, + const Clock *inst_clk, + // Return values. + PowerResult &result); void findInputInternalPower(const Pin *to_pin, - const LibertyPort *to_port, + LibertyPort *to_port, const Instance *inst, LibertyCell *cell, PwrActivity &to_activity, @@ -118,8 +131,7 @@ protected: const Corner *corner, // Return values. PowerResult &result); - void findOutputInternalPower(const Pin *to_pin, - const LibertyPort *to_port, + void findOutputInternalPower(const LibertyPort *to_port, const Instance *inst, LibertyCell *cell, PwrActivity &to_activity, @@ -132,18 +144,18 @@ protected: const Corner *corner, // Return values. PowerResult &result); - void findSwitchingPower(LibertyCell *cell, - const LibertyPort *to_port, - PwrActivity &activity, - float load_cap, - const Corner *corner, - // Return values. - PowerResult &result); + void findSwitchingPower(const Instance *inst, + LibertyCell *cell, + const Corner *corner, + const Clock *inst_clk, + // Return values. + PowerResult &result); float getSlew(Vertex *vertex, const RiseFall *rf, const Corner *corner); const Clock *findInstClk(const Instance *inst); const Clock *findClk(const Pin *to_pin); + float clockDuty(const Clock *clk); PwrActivity findClkedActivity(const Pin *pin, const Clock *inst_clk); PwrActivity findActivity(const Pin *pin); @@ -169,13 +181,12 @@ protected: const LibertyPort *cofactor_port, bool cofactor_positive); LibertyPort *findExprOutPort(FuncExpr *expr); - float findInputDuty(const Pin *to_pin, - const Instance *inst, + float findInputDuty(const Instance *inst, FuncExpr *func, InternalPower *pwr); - PwrActivity evalActivityDifference(FuncExpr *expr, - const Instance *inst, - const LibertyPort *cofactor_port); + float evalDiffDuty(FuncExpr *expr, + LibertyPort *from_port, + const Instance *inst); LibertyPort *findLinkPort(const LibertyCell *cell, const LibertyPort *corner_port); Pin *findLinkPin(const Instance *inst, @@ -186,6 +197,14 @@ protected: const Pin *&clk, const Pin *&gclk) const; + DdNode *funcBdd(const FuncExpr *expr); + DdNode *ensureNode(LibertyPort *port); + void clearVarMap(); + float evalBddActivity(DdNode *bdd, + const Instance *inst); + float evalBddDuty(DdNode *bdd, + const Instance *inst); + private: // Port/pin activities set by set_pin_activity. // set_pin_activity -global @@ -198,6 +217,11 @@ private: PwrActivityMap activity_map_; PwrSeqActivityMap seq_activity_map_; bool activities_valid_; + + DdManager *cudd_mgr_; + BddPortVarMap bdd_port_var_map_; + BddVarIdxPortMap bdd_var_idx_port_map_; + static constexpr int max_activity_passes_ = 100; friend class PropActivityVisitor; diff --git a/power/ReadVcdActivities.cc b/power/ReadVcdActivities.cc index f89b785e..853ef7bd 100644 --- a/power/ReadVcdActivities.cc +++ b/power/ReadVcdActivities.cc @@ -180,6 +180,7 @@ ReadVcdActivities::setVarActivity(const char *pin_name, { const Pin *pin = sdc_network_->findPin(pin_name); if (pin) { + debugPrint(debug_, "read_vcd_activities", 3, "%s values", pin_name); double transition_count, activity, duty; findVarActivity(var_values, value_bit, transition_count, activity, duty); @@ -191,11 +192,8 @@ ReadVcdActivities::setVarActivity(const char *pin_name, duty); if (sdc_->isLeafPinClock(pin)) checkClkPeriod(pin, transition_count); - else { - power_->setUserActivity(pin, activity, duty, - PwrActivityOrigin::user); - annotated_pins_.insert(pin); - } + power_->setUserActivity(pin, activity, duty, PwrActivityOrigin::vcd); + annotated_pins_.insert(pin); } } @@ -208,16 +206,13 @@ ReadVcdActivities::findVarActivity(const VcdValues &var_values, double &duty) { transition_count = 0.0; - char prev_value = var_values[0].value(); + char prev_value = var_values[0].value(value_bit); VcdTime prev_time = var_values[0].time(); VcdTime high_time = 0; for (const VcdValue &var_value : var_values) { VcdTime time = var_value.time(); - char value = var_value.value(); - if (value == '\0') { - uint64_t bus_value = var_value.busValue(); - value = ((bus_value >> value_bit) & 0x1) ? '1' : '0'; - } + char value = var_value.value(value_bit); + debugPrint(debug_, "read_vcd_activities", 3, " %llu %c", time, value); if (prev_value == '1') high_time += time - prev_time; if (value != prev_value) diff --git a/power/Vcd.cc b/power/Vcd.cc index 1ea9d8c3..a140528c 100644 --- a/power/Vcd.cc +++ b/power/Vcd.cc @@ -201,4 +201,13 @@ VcdValue::VcdValue(VcdTime time, { } +char +VcdValue::value(int value_bit) const +{ + if (value_ == '\0') + return ((bus_value_ >> value_bit) & 0x1) ? '1' : '0'; + else + return value_; +} + } diff --git a/power/Vcd.hh b/power/Vcd.hh index 76d904b5..b61d3935 100644 --- a/power/Vcd.hh +++ b/power/Vcd.hh @@ -146,6 +146,7 @@ public: VcdTime time() const { return time_; } char value() const { return value_; } uint64_t busValue() const { return bus_value_; } + char value(int value_bit) const; private: VcdTime time_; diff --git a/power/VcdReader.cc b/power/VcdReader.cc index 7d990dee..e30be19d 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -238,7 +238,7 @@ VcdReader::parseVarValues() string token = getToken(); while (!token.empty()) { char char0 = toupper(token[0]); - if (char0 == '#') { + if (char0 == '#' && token.size() > 1) { prev_time_ = time_; time_ = stoll(token.substr(1)); if (time_ > prev_time_) diff --git a/sdc/Clock.cc b/sdc/Clock.cc index dcc06fc3..5e87df91 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -118,8 +118,8 @@ void Clock::makeClkEdges() { clk_edges_ = new ClockEdge*[RiseFall::index_count]; - for (auto tr : RiseFall::range()) { - clk_edges_[tr->index()] = new ClockEdge(this, tr); + for (auto rf : RiseFall::range()) { + clk_edges_[rf->index()] = new ClockEdge(this, rf); } } diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 51e2e20e..c4c071d8 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -472,7 +472,7 @@ Sdc::operatingConditions(const MinMax *min_max) } const Pvt * -Sdc::pvt(Instance *inst, +Sdc::pvt(const Instance *inst, const MinMax *min_max) const { const InstancePvtMap &pvt_map = instance_pvt_maps_[min_max->index()]; diff --git a/search/Bfs.cc b/search/Bfs.cc index 8daf1d0c..d634cf2f 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -143,17 +143,19 @@ BfsIterator::visit(Level to_level, && levelLessOrEqual(first_level_, to_level)) { VertexSeq &level_vertices = queue_[first_level_]; incrLevel(first_level_); - if (!level_vertices.empty()) { - for (auto vertex : level_vertices) { - if (vertex) { - vertex->setBfsInQueue(bfs_index_, false); - visitor->visit(vertex); - visit_count++; - } + // Note that ArrivalVisitor::enqueueRefPinInputDelays may enqueue + // vertices at this level so range iteration fails if the vector grows. + while (!level_vertices.empty()) { + Vertex *vertex = level_vertices.back(); + level_vertices.pop_back(); + if (vertex) { + vertex->setBfsInQueue(bfs_index_, false); + visitor->visit(vertex); + visit_count++; } - level_vertices.clear(); - visitor->levelFinished(); } + level_vertices.clear(); + visitor->levelFinished(); } return visit_count; } diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index b73c7b89..58932034 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -31,7 +31,7 @@ #include "PortDirection.hh" #include "Corner.hh" #include "DcalcAnalysisPt.hh" -#include "dcalc/GraphDelayCalc1.hh" +#include "GraphDelayCalc.hh" #include "Sdc.hh" #include "StaState.hh" #include "Graph.hh" @@ -528,19 +528,19 @@ MakeTimingModel::findClkInsertionDelays() size_t clk_count = clks->size(); if (clk_count == 1) { for (const Clock *clk : *clks) { - TimingArcAttrsPtr attrs = nullptr; ClkDelays delays; sta_->findClkDelays(clk, delays); for (const MinMax *min_max : MinMax::range()) { + TimingArcAttrsPtr attrs = nullptr; for (const RiseFall *clk_rf : RiseFall::range()) { int clk_rf_index = clk_rf->index(); float delay = min_max->initValue(); for (const int end_rf_index : RiseFall::rangeIndex()) { - float delay1; + Delay delay1; bool exists; delays[clk_rf_index][end_rf_index].value(min_max, delay1, exists); if (exists) - delay = min_max->minMax(delay, delay1); + delay = min_max->minMax(delay, delayAsFloat(delay1)); } TimingModel *model = makeGateModelScalar(delay, clk_rf); if (attrs == nullptr) @@ -581,7 +581,7 @@ MakeTimingModel::makeScalarCheckModel(float value, library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *table_model = new TableModel(table, tbl_template, scale_factor_type, rf); - CheckTableModel *check_model = new CheckTableModel(table_model, nullptr); + CheckTableModel *check_model = new CheckTableModel(cell_, table_model, nullptr); return check_model; } @@ -598,7 +598,7 @@ MakeTimingModel::makeGateModelScalar(Delay delay, ScaleFactorType::cell, rf); TableModel *slew_model = new TableModel(slew_table, tbl_template, ScaleFactorType::cell, rf); - GateTableModel *gate_model = new GateTableModel(delay_model, nullptr, + GateTableModel *gate_model = new GateTableModel(cell_, delay_model, nullptr, slew_model, nullptr, nullptr, nullptr); return gate_model; @@ -613,7 +613,7 @@ MakeTimingModel::makeGateModelScalar(Delay delay, library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *delay_model = new TableModel(delay_table, tbl_template, ScaleFactorType::cell, rf); - GateTableModel *gate_model = new GateTableModel(delay_model, nullptr, + GateTableModel *gate_model = new GateTableModel(cell_, delay_model, nullptr, nullptr, nullptr, nullptr, nullptr); return gate_model; @@ -655,8 +655,7 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, float output_load_cap = graph_delay_calc_->loadCap(output_pin, dcalc_ap); ArcDelay drvr_self_delay; Slew drvr_self_slew; - drvr_gate_model->gateDelay(drvr_cell, pvt, in_slew1, - output_load_cap, 0.0, false, + drvr_gate_model->gateDelay(pvt, in_slew1, output_load_cap, 0.0, false, drvr_self_delay, drvr_self_slew); const TableModel *drvr_table = drvr_gate_model->delayModel(); @@ -671,8 +670,7 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, // get slew from driver input pin ArcDelay gate_delay; Slew gate_slew; - drvr_gate_model->gateDelay(drvr_cell, pvt, in_slew1, - load_cap, 0.0, false, + drvr_gate_model->gateDelay(pvt, in_slew1, load_cap, 0.0, false, gate_delay, gate_slew); // Remove the self delay driving the output pin net load cap. load_values->push_back(delayAsFloat(delay + gate_delay @@ -694,7 +692,8 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, ScaleFactorType::cell, rf); TableModel *slew_model = new TableModel(slew_table, model_template, ScaleFactorType::cell, rf); - GateTableModel *gate_model = new GateTableModel(delay_model, nullptr, + GateTableModel *gate_model = new GateTableModel(cell_, + delay_model, nullptr, slew_model, nullptr, nullptr, nullptr); return gate_model; diff --git a/search/Sim.cc b/search/Sim.cc index e18bdf08..24c98e2b 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -60,15 +60,14 @@ Sim::Sim(StaState *sta) : invalid_load_pins_(network_), instances_with_const_pins_(network_), instances_to_annotate_(network_), - // cacheSize = 2^15 - cudd_manager_(Cudd_Init(0, 0, CUDD_UNIQUE_SLOTS, 32768, 0)) + cudd_mgr_(Cudd_Init(0, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0)) { } Sim::~Sim() { delete observer_; - Cudd_Quit(cudd_manager_); + Cudd_Quit(cudd_mgr_); } #if CUDD @@ -88,11 +87,11 @@ Sim::functionSense(const FuncExpr *expr, LibertyPort *input_port = network_->libertyPort(input_pin); DdNode *input_node = ensureNode(input_port); unsigned int input_index = Cudd_NodeReadIndex(input_node); - increasing = (Cudd_Increasing(cudd_manager_, bdd, input_index) - == Cudd_ReadOne(cudd_manager_)); - decreasing = (Cudd_Decreasing(cudd_manager_, bdd, input_index) - == Cudd_ReadOne(cudd_manager_)); - Cudd_RecursiveDeref(cudd_manager_, bdd); + increasing = (Cudd_Increasing(cudd_mgr_, bdd, input_index) + == Cudd_ReadOne(cudd_mgr_)); + decreasing = (Cudd_Decreasing(cudd_mgr_, bdd, input_index) + == Cudd_ReadOne(cudd_mgr_)); + Cudd_RecursiveDeref(cudd_mgr_, bdd); clearSymtab(); } TimingSense sense; @@ -113,7 +112,7 @@ Sim::clearSymtab() const { for (auto name_node : symtab_) { DdNode *sym_node = name_node.second; - Cudd_RecursiveDeref(cudd_manager_, sym_node); + Cudd_RecursiveDeref(cudd_mgr_, sym_node); } symtab_.clear(); } @@ -125,12 +124,12 @@ Sim::evalExpr(const FuncExpr *expr, UniqueLock lock(cudd_lock_); DdNode *bdd = funcBdd(expr, inst); LogicValue value = LogicValue::unknown; - if (bdd == Cudd_ReadLogicZero(cudd_manager_)) + if (bdd == Cudd_ReadLogicZero(cudd_mgr_)) value = LogicValue::zero; - else if (bdd == Cudd_ReadOne(cudd_manager_)) + else if (bdd == Cudd_ReadOne(cudd_mgr_)) value = LogicValue::one; if (bdd) { - Cudd_RecursiveDeref(cudd_manager_, bdd); + Cudd_RecursiveDeref(cudd_mgr_, bdd); clearSymtab(); } return value; @@ -153,10 +152,10 @@ Sim::funcBdd(const FuncExpr *expr, LogicValue value = logicValue(pin); switch (value) { case LogicValue::zero: - result = Cudd_ReadLogicZero(cudd_manager_); + result = Cudd_ReadLogicZero(cudd_mgr_); break; case LogicValue::one: - result = Cudd_ReadOne(cudd_manager_); + result = Cudd_ReadOne(cudd_mgr_); break; default: result = ensureNode(port); @@ -174,7 +173,7 @@ Sim::funcBdd(const FuncExpr *expr, left = funcBdd(expr->left(), inst); right = funcBdd(expr->right(), inst); if (left && right) - result = Cudd_bddOr(cudd_manager_, left, right); + result = Cudd_bddOr(cudd_mgr_, left, right); else if (left) result = left; else if (right) @@ -184,7 +183,7 @@ Sim::funcBdd(const FuncExpr *expr, left = funcBdd(expr->left(), inst); right = funcBdd(expr->right(), inst); if (left && right) - result = Cudd_bddAnd(cudd_manager_, left, right); + result = Cudd_bddAnd(cudd_mgr_, left, right); else if (left) result = left; else if (right) @@ -194,17 +193,17 @@ Sim::funcBdd(const FuncExpr *expr, left = funcBdd(expr->left(), inst); right = funcBdd(expr->right(), inst); if (left && right) - result = Cudd_bddXor(cudd_manager_, left, right); + result = Cudd_bddXor(cudd_mgr_, left, right); else if (left) result = left; else if (right) result = right; break; case FuncExpr::op_one: - result = Cudd_ReadOne(cudd_manager_); + result = Cudd_ReadOne(cudd_mgr_); break; case FuncExpr::op_zero: - result = Cudd_ReadLogicZero(cudd_manager_); + result = Cudd_ReadLogicZero(cudd_mgr_); break; default: report_->critical(596, "unknown function operator"); @@ -212,9 +211,9 @@ Sim::funcBdd(const FuncExpr *expr, if (result) Cudd_Ref(result); if (left) - Cudd_RecursiveDeref(cudd_manager_, left); + Cudd_RecursiveDeref(cudd_mgr_, left); if (right) - Cudd_RecursiveDeref(cudd_manager_, right); + Cudd_RecursiveDeref(cudd_mgr_, right); return result; } @@ -224,7 +223,7 @@ Sim::ensureNode(LibertyPort *port) const const char *port_name = port->name(); DdNode *node = symtab_.findKey(port_name); if (node == nullptr) { - node = Cudd_bddNewVar(cudd_manager_); + node = Cudd_bddNewVar(cudd_mgr_); symtab_[port_name] = node; Cudd_Ref(node); } diff --git a/search/Sim.hh b/search/Sim.hh index 5344e77a..4f0a6690 100644 --- a/search/Sim.hh +++ b/search/Sim.hh @@ -129,16 +129,14 @@ protected: InstanceSet instances_with_const_pins_; InstanceSet instances_to_annotate_; -#ifdef CUDD DdNode *funcBdd(const FuncExpr *expr, const Instance *inst) const; DdNode *ensureNode(LibertyPort *port) const; void clearSymtab() const; - DdManager *cudd_manager_; + DdManager *cudd_mgr_; mutable BddSymbolTable symtab_; mutable std::mutex cudd_lock_; -#endif // CUDD }; // Abstract base class for Sim value change observer. diff --git a/search/Sta.cc b/search/Sta.cc index b886db80..bb437451 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -44,7 +44,7 @@ #include "parasitics/ReportParasiticAnnotation.hh" #include "DelayCalc.hh" #include "ArcDelayCalc.hh" -#include "dcalc/GraphDelayCalc1.hh" +#include "GraphDelayCalc.hh" #include "sdf/SdfWriter.hh" #include "Levelize.hh" #include "Sim.hh" @@ -414,7 +414,7 @@ Sta::makeArcDelayCalc() void Sta::makeGraphDelayCalc() { - graph_delay_calc_ = new GraphDelayCalc1(this); + graph_delay_calc_ = new GraphDelayCalc(this); } void @@ -3789,22 +3789,11 @@ Sta::connectedCap(const Pin *drvr_pin, float &pin_cap, float &wire_cap) const { - pin_cap = 0.0; - wire_cap = 0.0; - bool cap_exists = false; const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); Parasitic *parasitic = arc_delay_calc_->findParasitic(drvr_pin, rf, dcalc_ap); - float ap_pin_cap = 0.0; - float ap_wire_cap = 0.0; graph_delay_calc_->loadCap(drvr_pin, parasitic, rf, dcalc_ap, - ap_pin_cap, ap_wire_cap); + pin_cap, wire_cap); arc_delay_calc_->finishDrvrPin(); - if (!cap_exists - || min_max->compare(ap_pin_cap, pin_cap)) { - pin_cap = ap_pin_cap; - wire_cap = ap_wire_cap; - cap_exists = true; - } } void