// OpenSTA, Static Timing Analyzer
// Copyright (c) 2019, 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 <https://www.gnu.org/licenses/>.
#include "Machine.hh"
#include "Wireload.hh"
#include "Liberty.hh"
#include "Parasitics.hh"
#include "Network.hh"
#include "Sdc.hh"
#include "EstimateParasitics.hh"
namespace sta {
// For multiple driver nets, output pin capacitances are treated as
// loads when driven by a different pin.
void
EstimateParasitics::estimatePiElmore(const Pin *drvr_pin,
const TransRiseFall *tr,
const Wireload *wireload,
float fanout,
float net_pin_cap,
const OperatingConditions *op_cond,
const Corner *corner,
const MinMax *min_max,
const StaState *sta,
float &c2,
float &rpi,
float &c1,
float &elmore_res,
float &elmore_cap,
bool &elmore_use_load_cap)
{
float wireload_cap, wireload_res;
wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res);
WireloadTree tree = WireloadTree::unknown;
if (op_cond)
tree = op_cond->wireloadTree();
switch (tree) {
case WireloadTree::worst_case:
estimatePiElmoreWorst(drvr_pin, wireload_cap, wireload_res,
fanout, net_pin_cap, tr, op_cond, corner,
min_max, sta,
c2, rpi, c1, elmore_res,
elmore_cap, elmore_use_load_cap);
break;
case WireloadTree::balanced:
case WireloadTree::unknown:
estimatePiElmoreBalanced(drvr_pin, wireload_cap, wireload_res,
fanout, net_pin_cap, tr, op_cond,
corner, min_max,sta,
c2, rpi, c1, elmore_res,
elmore_cap, elmore_use_load_cap);
break;
case WireloadTree::best_case:
estimatePiElmoreBest(drvr_pin, wireload_cap, net_pin_cap, tr,
op_cond, corner, min_max,
c2, rpi, c1, elmore_res, elmore_cap,
elmore_use_load_cap);
break;
}
}
// No wire resistance, so load is lumped capacitance.
void
EstimateParasitics::estimatePiElmoreBest(const Pin *,
float wireload_cap,
float net_pin_cap,
const TransRiseFall *,
const OperatingConditions *,
const Corner *,
const MinMax *,
float &c2,
float &rpi,
float &c1,
float &elmore_res,
float &elmore_cap,
bool &elmore_use_load_cap) const
{
c2 = wireload_cap + net_pin_cap;
rpi = 0.0;
c1 = 0.0;
elmore_res = 0.0;
elmore_cap = 0.0;
elmore_use_load_cap = false;
}
// All load capacitance (except driver pin cap) is on the far side of
// the resistor.
void
EstimateParasitics::estimatePiElmoreWorst(const Pin *drvr_pin,
float wireload_cap,
float wireload_res,
float,
float net_pin_cap,
const TransRiseFall *tr,
const OperatingConditions *op_cond,
const Corner *corner,
const MinMax *min_max,
const StaState *sta,
float &c2,
float &rpi,
float &c1,
float &elmore_res,
float &elmore_cap,
bool &elmore_use_load_cap)
{
Sdc *sdc = sta->sdc();
float drvr_pin_cap = 0.0;
drvr_pin_cap = sdc->pinCapacitance(drvr_pin, tr, op_cond, corner, min_max);
c2 = drvr_pin_cap;
rpi = wireload_res;
c1 = net_pin_cap - drvr_pin_cap + wireload_cap;
elmore_res = wireload_res;
elmore_cap = c1;
elmore_use_load_cap = false;
}
// Each load capacitance and wireload cap/fanout has resistance/fanout
// connecting it to the driver.
// Use O'Brien/Savarino reduction to rspf (pi elmore) model.
void
EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin,
float wireload_cap,
float wireload_res,
float fanout,
float net_pin_cap,
const TransRiseFall *tr,
const OperatingConditions *op_cond,
const Corner *corner,
const MinMax *min_max,
const StaState *sta,
float &c2,
float &rpi,
float &c1,
float &elmore_res,
float &elmore_cap,
bool &elmore_use_load_cap)
{
if (wireload_res == 0.0) {
// No resistance, so load is capacitance only.
c2 = wireload_cap + net_pin_cap;
rpi = 0.0;
c1 = 0.0;
elmore_res = 0.0;
elmore_cap = 0.0;
elmore_use_load_cap = false;
}
else {
Sdc *sdc = sta->sdc();
Network *network = sta->network();
double res_fanout = wireload_res / fanout;
double cap_fanout = wireload_cap / fanout;
// Find admittance moments.
double y1 = 0.0;
double y2 = 0.0;
double y3 = 0.0;
y1 = sdc->pinCapacitance(drvr_pin, tr, op_cond, corner, min_max);
PinConnectedPinIterator *load_iter =
network->connectedPinIterator(drvr_pin);
while (load_iter->hasNext()) {
const Pin *load_pin = load_iter->next();
// Bidirects don't count themselves as loads.
if (load_pin != drvr_pin && network->isLoad(load_pin)) {
Port *port = network->port(load_pin);
double load_cap = 0.0;
if (network->isLeaf(load_pin))
load_cap = sdc->pinCapacitance(load_pin, tr, op_cond,
corner, min_max);
else if (network->isTopLevelPort(load_pin))
load_cap = sdc->portExtCap(port, tr, min_max);
else
internalError("load pin not leaf or top level");
double cap = load_cap + cap_fanout;
double y2_ = res_fanout * cap * cap;
y1 += cap;
y2 += -y2_;
y3 += y2_ * res_fanout * cap;
}
}
delete load_iter;
if (y3 == 0) {
// No loads.
c1 = 0.0;
c2 = 0.0;
rpi = 0.0;
}
else {
c1 = static_cast<float>(y2 * y2 / y3);
c2 = static_cast<float>(y1 - y2 * y2 / y3);
rpi = static_cast<float>(-y3 * y3 / (y2 * y2 * y2));
}
elmore_res = static_cast<float>(res_fanout);
elmore_cap = static_cast<float>(cap_fanout);
elmore_use_load_cap = true;
}
}
#if 0
static void
selectWireload(Network *network)
{
// Look for a default wireload selection group.
WireloadSelection *selection;
float area = instanceArea(network->topInstance(), network);
Wireload *wireload = selection->findWireload(area);
}
static float
instanceArea(Instance *inst,
Network *network)
{
float area = 0.0;
LeafInstanceIterator *inst_iter = network->leafInstanceIterator();
while (network->hasNext(inst_iter)) {
Instance *leaf = network->next(inst_iter);
area += network->cell(leaf)->area();
}
return area;
}
#endif
} // namespace