2018-09-28 17:54:21 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2020-03-07 03:50:37 +01:00
|
|
|
// Copyright (c) 2020, Parallax Software, Inc.
|
2018-09-28 17:54:21 +02:00
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "LumpedCapDelayCalc.hh"
|
2020-04-05 20:35:51 +02:00
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "Debug.hh"
|
|
|
|
|
#include "Units.hh"
|
|
|
|
|
#include "TimingArc.hh"
|
|
|
|
|
#include "TimingModel.hh"
|
|
|
|
|
#include "Liberty.hh"
|
|
|
|
|
#include "Network.hh"
|
|
|
|
|
#include "Sdc.hh"
|
|
|
|
|
#include "Parasitics.hh"
|
|
|
|
|
#include "DcalcAnalysisPt.hh"
|
|
|
|
|
#include "GraphDelayCalc.hh"
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
ArcDelayCalc *
|
|
|
|
|
makeLumpedCapDelayCalc(StaState *sta)
|
|
|
|
|
{
|
|
|
|
|
return new LumpedCapDelayCalc(sta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LumpedCapDelayCalc::LumpedCapDelayCalc(StaState *sta) :
|
|
|
|
|
ArcDelayCalc(sta)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArcDelayCalc *
|
|
|
|
|
LumpedCapDelayCalc::copy()
|
|
|
|
|
{
|
|
|
|
|
return new LumpedCapDelayCalc(this);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-11 05:36:48 +02:00
|
|
|
Parasitic *
|
2018-09-28 17:54:21 +02:00
|
|
|
LumpedCapDelayCalc::findParasitic(const Pin *drvr_pin,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2019-04-11 05:36:48 +02:00
|
|
|
const DcalcAnalysisPt *dcalc_ap)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
// set_load has precidence over parasitics.
|
|
|
|
|
if (!sdc_->drvrPinHasWireCap(drvr_pin)) {
|
2019-04-19 03:01:10 +02:00
|
|
|
Parasitic *parasitic = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt();
|
2019-04-19 03:01:10 +02:00
|
|
|
if (parasitics_->haveParasitics()) {
|
|
|
|
|
// Prefer PiElmore.
|
2019-11-11 23:30:19 +01:00
|
|
|
parasitic = parasitics_->findPiElmore(drvr_pin, rf, parasitic_ap);
|
2019-04-19 03:01:10 +02:00
|
|
|
if (parasitic)
|
|
|
|
|
return parasitic;
|
|
|
|
|
|
|
|
|
|
Parasitic *parasitic_network =
|
|
|
|
|
parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap);
|
|
|
|
|
if (parasitic_network) {
|
|
|
|
|
parasitics_->reduceToPiElmore(parasitic_network, drvr_pin,
|
|
|
|
|
dcalc_ap->operatingConditions(),
|
|
|
|
|
dcalc_ap->corner(),
|
|
|
|
|
dcalc_ap->constraintMinMax(),
|
|
|
|
|
parasitic_ap);
|
2019-11-11 23:30:19 +01:00
|
|
|
parasitic = parasitics_->findPiElmore(drvr_pin, rf, parasitic_ap);
|
2019-04-19 03:01:10 +02:00
|
|
|
reduced_parasitic_drvrs_.push_back(drvr_pin);
|
|
|
|
|
return parasitic;
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2019-04-11 05:36:48 +02:00
|
|
|
|
|
|
|
|
const MinMax *cnst_min_max = dcalc_ap->constraintMinMax();
|
|
|
|
|
Wireload *wireload = sdc_->wireloadDefaulted(cnst_min_max);
|
|
|
|
|
if (wireload) {
|
|
|
|
|
float pin_cap, wire_cap, fanout;
|
|
|
|
|
bool has_wire_cap;
|
2019-11-11 23:30:19 +01:00
|
|
|
graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap,
|
2019-04-11 05:36:48 +02:00
|
|
|
pin_cap, wire_cap, fanout, has_wire_cap);
|
2019-11-11 23:30:19 +01:00
|
|
|
parasitic = parasitics_->estimatePiElmore(drvr_pin, rf, wireload,
|
2019-04-11 05:36:48 +02:00
|
|
|
fanout, pin_cap,
|
|
|
|
|
dcalc_ap->operatingConditions(),
|
|
|
|
|
dcalc_ap->corner(),
|
|
|
|
|
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.
|
|
|
|
|
unsaved_parasitics_.push_back(parasitic);
|
|
|
|
|
return parasitic;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-11 05:36:48 +02:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2019-04-11 05:36:48 +02:00
|
|
|
LumpedCapDelayCalc::finishDrvrPin()
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2019-04-11 05:36:48 +02:00
|
|
|
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();
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
LumpedCapDelayCalc::inputPortDelay(const Pin *, float in_slew,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
Parasitic *,
|
|
|
|
|
const DcalcAnalysisPt *)
|
|
|
|
|
{
|
|
|
|
|
drvr_slew_ = in_slew;
|
2019-11-11 23:30:19 +01:00
|
|
|
drvr_rf_ = rf;
|
2018-09-28 17:54:21 +02:00
|
|
|
drvr_library_ = network_->defaultLibertyLibrary();
|
|
|
|
|
multi_drvr_slew_factor_ = 1.0F;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-26 18:15:52 +01:00
|
|
|
float
|
|
|
|
|
LumpedCapDelayCalc::ceff(const LibertyCell *,
|
|
|
|
|
TimingArc *,
|
|
|
|
|
const Slew &,
|
|
|
|
|
float load_cap,
|
|
|
|
|
Parasitic *,
|
|
|
|
|
float,
|
|
|
|
|
const Pvt *,
|
|
|
|
|
const DcalcAnalysisPt *)
|
|
|
|
|
{
|
|
|
|
|
return load_cap;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
void
|
|
|
|
|
LumpedCapDelayCalc::gateDelay(const LibertyCell *drvr_cell,
|
|
|
|
|
TimingArc *arc,
|
|
|
|
|
const Slew &in_slew,
|
|
|
|
|
float load_cap,
|
|
|
|
|
Parasitic *,
|
|
|
|
|
float related_out_cap,
|
|
|
|
|
const Pvt *pvt,
|
|
|
|
|
const DcalcAnalysisPt *dcalc_ap,
|
|
|
|
|
// Return values.
|
|
|
|
|
ArcDelay &gate_delay,
|
|
|
|
|
Slew &drvr_slew)
|
|
|
|
|
{
|
|
|
|
|
GateTimingModel *model = gateModel(arc, dcalc_ap);
|
|
|
|
|
debugPrint3(debug_, "delay_calc", 3,
|
|
|
|
|
" in_slew = %s load_cap = %s related_load_cap = %s lumped\n",
|
2019-01-27 08:03:01 +01:00
|
|
|
delayAsString(in_slew, this),
|
2018-09-28 17:54:21 +02:00
|
|
|
units()->capacitanceUnit()->asString(load_cap),
|
|
|
|
|
units()->capacitanceUnit()->asString(related_out_cap));
|
|
|
|
|
if (model) {
|
2018-11-26 18:15:52 +01:00
|
|
|
ArcDelay gate_delay1;
|
|
|
|
|
Slew drvr_slew1;
|
2018-09-28 17:54:21 +02:00
|
|
|
float in_slew1 = delayAsFloat(in_slew);
|
|
|
|
|
model->gateDelay(drvr_cell, pvt, in_slew1, load_cap, related_out_cap,
|
2019-01-27 08:03:01 +01:00
|
|
|
pocv_enabled_, gate_delay1, drvr_slew1);
|
2018-09-28 17:54:21 +02:00
|
|
|
gate_delay = gate_delay1;
|
|
|
|
|
drvr_slew = drvr_slew1;
|
|
|
|
|
drvr_slew_ = drvr_slew1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
gate_delay = delay_zero;
|
|
|
|
|
drvr_slew = delay_zero;
|
|
|
|
|
drvr_slew_ = 0.0;
|
|
|
|
|
}
|
2019-11-11 23:30:19 +01:00
|
|
|
drvr_rf_ = arc->toTrans()->asRiseFall();
|
2018-09-28 17:54:21 +02:00
|
|
|
drvr_library_ = drvr_cell->libertyLibrary();
|
|
|
|
|
multi_drvr_slew_factor_ = 1.0F;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
LumpedCapDelayCalc::loadDelay(const Pin *load_pin,
|
|
|
|
|
ArcDelay &wire_delay,
|
|
|
|
|
Slew &load_slew)
|
|
|
|
|
{
|
|
|
|
|
Delay wire_delay1 = 0.0;
|
|
|
|
|
Slew load_slew1 = drvr_slew_ * multi_drvr_slew_factor_;
|
|
|
|
|
thresholdAdjust(load_pin, wire_delay1, load_slew1);
|
|
|
|
|
wire_delay = wire_delay1;
|
|
|
|
|
load_slew = load_slew1 * multi_drvr_slew_factor_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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_) {
|
2019-11-11 23:30:19 +01:00
|
|
|
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_);
|
2018-09-28 17:54:21 +02:00
|
|
|
float load_delay_delta =
|
|
|
|
|
delayAsFloat(load_slew) * ((load_vth - drvr_vth) / drvr_slew_delta);
|
2019-11-11 23:30:19 +01:00
|
|
|
load_delay += (drvr_rf_ == RiseFall::rise())
|
2018-09-28 17:54:21 +02:00
|
|
|
? load_delay_delta
|
|
|
|
|
: -load_delay_delta;
|
2019-11-11 23:30:19 +01:00
|
|
|
float load_slew_delta = load_library->slewUpperThreshold(drvr_rf_)
|
|
|
|
|
- load_library->slewLowerThreshold(drvr_rf_);
|
2018-09-28 17:54:21 +02:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
LumpedCapDelayCalc::reportGateDelay(const LibertyCell *drvr_cell,
|
|
|
|
|
TimingArc *arc,
|
|
|
|
|
const Slew &in_slew,
|
|
|
|
|
float load_cap,
|
|
|
|
|
Parasitic *,
|
|
|
|
|
float related_out_cap,
|
|
|
|
|
const Pvt *pvt,
|
|
|
|
|
const DcalcAnalysisPt *dcalc_ap,
|
|
|
|
|
int digits,
|
|
|
|
|
string *result)
|
|
|
|
|
{
|
|
|
|
|
GateTimingModel *model = gateModel(arc, dcalc_ap);
|
|
|
|
|
if (model) {
|
|
|
|
|
float in_slew1 = delayAsFloat(in_slew);
|
|
|
|
|
model->reportGateDelay(drvr_cell, pvt, in_slew1, load_cap,
|
2019-01-27 08:03:01 +01:00
|
|
|
related_out_cap, false, digits, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-26 18:15:52 +01:00
|
|
|
void
|
2018-09-28 17:54:21 +02:00
|
|
|
LumpedCapDelayCalc::checkDelay(const LibertyCell *cell,
|
|
|
|
|
TimingArc *arc,
|
|
|
|
|
const Slew &from_slew,
|
|
|
|
|
const Slew &to_slew,
|
|
|
|
|
float related_out_cap,
|
|
|
|
|
const Pvt *pvt,
|
2018-11-26 18:15:52 +01:00
|
|
|
const DcalcAnalysisPt *dcalc_ap,
|
|
|
|
|
// Return values.
|
|
|
|
|
ArcDelay &margin)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
CheckTimingModel *model = checkModel(arc, dcalc_ap);
|
|
|
|
|
if (model) {
|
|
|
|
|
float from_slew1 = delayAsFloat(from_slew);
|
|
|
|
|
float to_slew1 = delayAsFloat(to_slew);
|
2019-01-27 08:03:01 +01:00
|
|
|
model->checkDelay(cell, pvt, from_slew1, to_slew1, related_out_cap,
|
2019-06-04 17:12:22 +02:00
|
|
|
pocv_enabled_, margin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else
|
2018-11-26 18:15:52 +01:00
|
|
|
margin = delay_zero;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
LumpedCapDelayCalc::reportCheckDelay(const LibertyCell *cell,
|
|
|
|
|
TimingArc *arc,
|
|
|
|
|
const Slew &from_slew,
|
|
|
|
|
const char *from_slew_annotation,
|
|
|
|
|
const Slew &to_slew,
|
|
|
|
|
float related_out_cap,
|
|
|
|
|
const Pvt *pvt,
|
|
|
|
|
const DcalcAnalysisPt *dcalc_ap,
|
|
|
|
|
int digits,
|
|
|
|
|
string *result)
|
|
|
|
|
{
|
|
|
|
|
CheckTimingModel *model = checkModel(arc, dcalc_ap);
|
|
|
|
|
if (model) {
|
|
|
|
|
float from_slew1 = delayAsFloat(from_slew);
|
|
|
|
|
float to_slew1 = delayAsFloat(to_slew);
|
|
|
|
|
model->reportCheckDelay(cell, pvt, from_slew1, from_slew_annotation, to_slew1,
|
2019-01-27 08:03:01 +01:00
|
|
|
related_out_cap, false, digits, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
LumpedCapDelayCalc::setMultiDrvrSlewFactor(float factor)
|
|
|
|
|
{
|
|
|
|
|
multi_drvr_slew_factor_ = factor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|