213 lines
6.2 KiB
C++
213 lines
6.2 KiB
C++
// OpenSTA, Static Timing Analyzer
|
|
// Copyright (c) 2020, 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 "Parasitics.hh"
|
|
|
|
#include "Error.hh"
|
|
#include "Debug.hh"
|
|
#include "Liberty.hh"
|
|
#include "Wireload.hh"
|
|
#include "Network.hh"
|
|
#include "PortDirection.hh"
|
|
#include "Sdc.hh"
|
|
#include "ReduceParasitics.hh"
|
|
|
|
namespace sta {
|
|
|
|
Parasitics::Parasitics(StaState *sta) :
|
|
StaState(sta)
|
|
{
|
|
}
|
|
|
|
Net *
|
|
Parasitics::findParasiticNet(const Pin *pin) const
|
|
{
|
|
Net *net = network_->net(pin);
|
|
// Pins on the top level instance may not have nets.
|
|
// Use the net connected to the pin's terminal.
|
|
if (net == nullptr && network_->isTopLevelPort(pin)) {
|
|
Term *term = network_->term(pin);
|
|
if (term)
|
|
return network_->net(term);
|
|
else
|
|
return nullptr;
|
|
}
|
|
if (net)
|
|
return network_->highestConnectedNet(net);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
Parasitics::check(Parasitic *) const
|
|
{
|
|
#if 0
|
|
ConcreteParasiticSubNodeMap::Iterator sub_node_iter(sub_nodes_);
|
|
while (sub_node_iter.hasNext()) {
|
|
ConcreteParasiticSubNode *node = sub_node_iter.next();
|
|
ConcreteParasiticDeviceSeq::Iterator device_iter(node->devices());
|
|
int res_count = 0;
|
|
while (device_iter.hasNext()) {
|
|
ConcreteParasiticDevice *device = device_iter.next();
|
|
if (device->isResistor())
|
|
res_count++;
|
|
}
|
|
if (res_count == 0)
|
|
report->warn("sub node %s has no resistor connections\n",
|
|
node->name(network));
|
|
else if (res_count == 1)
|
|
report->warn("sub node %s has one resistor connection\n",
|
|
node->name(network));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
Parasitic *
|
|
Parasitics::makeWireloadNetwork(const Pin *drvr_pin,
|
|
const Wireload *wireload,
|
|
float fanout,
|
|
const OperatingConditions *op_cond,
|
|
const ParasiticAnalysisPt *ap)
|
|
{
|
|
Net *net = network_->net(drvr_pin);
|
|
Parasitic *parasitic = makeParasiticNetwork(net, false, ap);
|
|
float wireload_cap, wireload_res;
|
|
wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res);
|
|
|
|
WireloadTree tree = WireloadTree::balanced;
|
|
if (op_cond)
|
|
tree = op_cond->wireloadTree();
|
|
switch (tree) {
|
|
case WireloadTree::worst_case:
|
|
makeWireloadNetworkWorst(parasitic, drvr_pin, wireload_cap,
|
|
wireload_res, fanout, ap);
|
|
break;
|
|
case WireloadTree::balanced:
|
|
makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap,
|
|
wireload_res, fanout, ap);
|
|
break;
|
|
case WireloadTree::best_case:
|
|
case WireloadTree::unknown:
|
|
makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap,
|
|
wireload_res, fanout, ap);
|
|
break;
|
|
}
|
|
return parasitic;
|
|
}
|
|
|
|
// All load capacitance (except driver pin cap) is on the far side of
|
|
// the resistor.
|
|
void
|
|
Parasitics::makeWireloadNetworkWorst(Parasitic *parasitic,
|
|
const Pin *drvr_pin,
|
|
float wireload_cap,
|
|
float wireload_res,
|
|
float /* fanout */,
|
|
const ParasiticAnalysisPt *ap)
|
|
{
|
|
ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin);
|
|
Net *net = network_->net(drvr_pin);
|
|
ParasiticNode *load_node = ensureParasiticNode(parasitic, net, 0);
|
|
makeResistor(nullptr, drvr_node, load_node, wireload_res, ap);
|
|
parasitics_->incrCap(load_node, wireload_cap, ap);
|
|
PinConnectedPinIterator *load_iter =
|
|
network_->connectedPinIterator(drvr_pin);
|
|
while (load_iter->hasNext()) {
|
|
const Pin *load_pin = load_iter->next();
|
|
if (load_pin != drvr_pin
|
|
&& network_->isLoad(load_pin)) {
|
|
ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin);
|
|
makeResistor(nullptr, load_node, load_node1, 0.0, ap);
|
|
}
|
|
}
|
|
}
|
|
|
|
// No wire resistance, so load is lumped capacitance.
|
|
void
|
|
Parasitics::makeWireloadNetworkBest(Parasitic *parasitic,
|
|
const Pin *drvr_pin,
|
|
float wireload_cap,
|
|
float /* wireload_res */,
|
|
float /* fanout */,
|
|
const ParasiticAnalysisPt *ap)
|
|
{
|
|
ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin);
|
|
parasitics_->incrCap(drvr_node, wireload_cap, ap);
|
|
PinConnectedPinIterator *load_iter =
|
|
network_->connectedPinIterator(drvr_pin);
|
|
while (load_iter->hasNext()) {
|
|
const Pin *load_pin = load_iter->next();
|
|
if (load_pin != drvr_pin
|
|
&& network_->isLoad(load_pin)) {
|
|
ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin);
|
|
makeResistor(nullptr, drvr_node, load_node1, 0.0, ap);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Each load capacitance and wireload cap/fanout has resistance/fanout
|
|
// connecting it to the driver.
|
|
void
|
|
Parasitics::makeWireloadNetworkBalanced(Parasitic *parasitic,
|
|
const Pin *drvr_pin,
|
|
float wireload_cap,
|
|
float wireload_res,
|
|
float fanout,
|
|
const ParasiticAnalysisPt *ap)
|
|
{
|
|
float fanout_cap = wireload_cap / fanout;
|
|
float fanout_res = wireload_res / fanout;
|
|
ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin);
|
|
PinConnectedPinIterator *load_iter =
|
|
network_->connectedPinIterator(drvr_pin);
|
|
while (load_iter->hasNext()) {
|
|
const Pin *load_pin = load_iter->next();
|
|
if (load_pin != drvr_pin
|
|
&& network_->isLoad(load_pin)) {
|
|
ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin);
|
|
makeResistor(nullptr, drvr_node, load_node1, fanout_res, ap);
|
|
parasitics_->incrCap(load_node1, fanout_cap, ap);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
ParasiticAnalysisPt::ParasiticAnalysisPt(const char *name,
|
|
int index,
|
|
const MinMax *min_max) :
|
|
name_(stringCopy(name)),
|
|
index_(index),
|
|
min_max_(min_max),
|
|
coupling_cap_factor_(1.0)
|
|
{
|
|
}
|
|
|
|
ParasiticAnalysisPt::~ParasiticAnalysisPt()
|
|
{
|
|
stringDelete(name_);
|
|
}
|
|
|
|
void
|
|
ParasiticAnalysisPt::setCouplingCapFactor(float factor)
|
|
{
|
|
coupling_cap_factor_ = factor;
|
|
}
|
|
|
|
} // namespace
|