prima merge noise branch changes and rm iterator uses

Signed-off-by: James Cherry <cherry@CerezoBook.local>
This commit is contained in:
James Cherry 2026-07-27 16:02:29 -07:00
parent 87ce5680df
commit f29b6a43c6
2 changed files with 174 additions and 105 deletions

View File

@ -48,6 +48,13 @@ namespace sta {
// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998
// McGraw-Hill, Inc. New York, NY.
// "PRIMA: Passive Reduced-order Interconnect Macromodeling Algorithm",
// Altan Odabasioglu, Mustafa Celik, and Lawrence T. Pileggi
// IEEE Transactions on Computer-Aided Design of Integrated Circuits and Systems,
// vol. 17, no. 8, August 1998
using ParasiticSet = std::set<const Parasitic*>;
ArcDelayCalc *
makePrimaDelayCalc(StaState *sta)
{
@ -460,105 +467,68 @@ PrimaDelayCalc::initSim()
void
PrimaDelayCalc::findNodeCount()
{
includes_pin_caps_ = parasitics_->includesPinCaps(parasitic_network_);
coupling_cap_multiplier_ = 1.0;
node_capacitances_.clear();
pin_node_map_.clear();
node_index_map_.clear();
node_count_ = 0;
// Collect the nodes that enter G by walking out from the drivers through
// resistors. G is conductance-only, so a node with no resistive path to a
// driver has an all-zero row which is dropped to prevent singularity.
ParasiticNodeResistorMap resistor_map =
parasitics_->parasiticNodeResistorMap(parasitic_network_);
std::vector<ParasiticNode *> queue;
for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) {
const Pin *drvr_pin = (*dcalc_args_)[drvr_idx].drvrPin();
ParasiticNode *drvr_node =
parasitics_->findParasiticNode(parasitic_network_, drvr_pin);
if (drvr_node && !parasitics_->isExternal(drvr_node)
&& !node_index_map_.contains(drvr_node))
placeNode(drvr_node, node_capacitances_.size(), queue);
}
while (!queue.empty()) {
ParasiticNode *node = queue.back();
queue.pop_back();
size_t node_index = node_index_map_[node];
auto resistor_itr = resistor_map.find(node);
if (resistor_itr != resistor_map.end()) {
for (ParasiticResistor *resistor : resistor_itr->second) {
ParasiticNode *next_node = parasitics_->otherNode(resistor, node);
if (next_node
&& !parasitics_->isExternal(next_node)
&& !node_index_map_.contains(next_node)) {
bool shorted = parasitics_->value(resistor) <= 0.0;
placeNode(next_node, shorted ? node_index : node_capacitances_.size(),
queue);
ParasiticSet visited_parasitics;
for (const ArcDcalcArg &dcalc_arg : *dcalc_args_) {
const Parasitic *parasitic = dcalc_arg.parasitic();
if (!visited_parasitics.contains(parasitic)) {
ParasiticNodeResistorMap resistor_map =
parasitics_->parasiticNodeResistorMap(parasitic);
std::vector<ParasiticNode *> queue;
for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) {
const Pin *drvr_pin = (*dcalc_args_)[drvr_idx].drvrPin();
ParasiticNode *drvr_node =
parasitics_->findParasiticNode(parasitic, drvr_pin);
if (drvr_node && !parasitics_->isExternal(drvr_node)
&& !node_index_map_.contains(drvr_node)) {
placeNode(drvr_node, node_count_++);
queue.push_back(drvr_node);
}
}
while (!queue.empty()) {
ParasiticNode *node = queue.back();
queue.pop_back();
size_t node_index = node_index_map_[node];
auto resistor_itr = resistor_map.find(node);
if (resistor_itr != resistor_map.end()) {
for (ParasiticResistor *resistor : resistor_itr->second) {
ParasiticNode *next_node = parasitics_->otherNode(resistor, node);
if (next_node
&& !parasitics_->isExternal(next_node)
&& !node_index_map_.contains(next_node)) {
bool shorted = parasitics_->value(resistor) == 0;
placeNode(next_node, shorted ? node_index : node_count_++);
queue.push_back(next_node);
}
}
}
}
visited_parasitics.insert(parasitic);
}
}
// Lump each coupling capacitor to ground at its internal (non-external)
// nodes that made it into the network.
for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) {
float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_;
ParasiticNode *node1 = parasitics_->node1(capacitor);
if (node1 && !parasitics_->isExternal(node1)) {
auto itr = node_index_map_.find(node1);
if (itr != node_index_map_.end())
node_capacitances_[itr->second] += cap;
}
ParasiticNode *node2 = parasitics_->node2(capacitor);
if (node2 && !parasitics_->isExternal(node2)) {
auto itr = node_index_map_.find(node2);
if (itr != node_index_map_.end())
node_capacitances_[itr->second] += cap;
}
}
node_count_ = node_capacitances_.size();
}
// Add node to the conductance system at index (shared by drivers and by the
// resistor walk); a merged short reuses its near node's index. Accumulates the
// node's ground capacitance and queues it for the walk.
// Add node to network at index (shared by drivers and by the
// resistor walk). A merged short reuses the near node's index.
void
PrimaDelayCalc::placeNode(ParasiticNode *node,
size_t index,
std::vector<ParasiticNode*> &queue)
size_t index)
{
node_index_map_[node] = index;
if (index == node_capacitances_.size())
node_capacitances_.push_back(0.0);
node_capacitances_[index] +=
parasitics_->nodeGndCap(node) + pinCapacitance(node);
const Pin *pin = parasitics_->pin(node);
if (pin) {
pin_node_map_[pin] = index;
debugPrint(debug_, "ccs_dcalc", 1, "pin {} node {}",
network_->pathName(pin), index);
}
queue.push_back(node);
}
float
PrimaDelayCalc::pinCapacitance(ParasiticNode *node)
{
const Pin *pin = parasitics_->pin(node);
float pin_cap = 0.0;
const Sdc *sdc = scene_->sdc();
if (pin) {
Port *port = network_->port(pin);
LibertyPort *lib_port = network_->libertyPort(port);
if (lib_port) {
if (!includes_pin_caps_)
pin_cap = sdc->pinCapacitance(pin, drvr_rf_, scene_, min_max_);
}
else if (network_->isTopLevelPort(pin))
pin_cap = sdc->portExtCap(port, drvr_rf_, min_max_);
}
return pin_cap;
}
void
@ -600,6 +570,16 @@ PrimaDelayCalc::setXinit()
x_init_[node_count_ + p] = drvr_init_volt;
}
std::pair<size_t, bool>
PrimaDelayCalc::nodeIndex(const ParasiticNode *node)
{
auto node_index = node_index_map_.find(node);
if (node_index != node_index_map_.end())
return {node_index->second, true};
else
return {0, false};
}
void
PrimaDelayCalc::stampEqns()
{
@ -607,35 +587,23 @@ PrimaDelayCalc::stampEqns()
C_.setZero();
B_.setZero();
for (size_t node_idx = 0; node_idx < node_count_; node_idx++)
stampCapacitance(node_idx, node_capacitances_[node_idx]);
resistance_sum_ = 0.0;
for (ParasiticResistor *resistor : parasitics_->resistors(parasitic_network_)) {
auto itr1 = node_index_map_.find(parasitics_->node1(resistor));
auto itr2 = node_index_map_.find(parasitics_->node2(resistor));
// Skip a resistor with a node left out of the network.
if (itr1 == node_index_map_.end() || itr2 == node_index_map_.end())
continue;
size_t node_idx1 = itr1->second;
size_t node_idx2 = itr2->second;
float resistance = parasitics_->value(resistor);
// Skip a self loop / merged short (same index) or a non-positive (short)
// resistance; stamping 1/resistance would be infinite.
if (node_idx1 != node_idx2 && resistance > 0.0) {
stampConductance(node_idx1, node_idx2, 1.0 / resistance);
resistance_sum_ += resistance;
}
NetSet drvr_nets(network_);
for (ArcDcalcArg &dcalc_arg : *dcalc_args_) {
const Net *net = dcalc_arg.drvrNet(network_);
drvr_nets.insert(net);
}
resistance_sum_ = 0.0;
ParasiticSet visited_parasitics;
for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) {
const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx];
size_t drvr_node = pin_node_map_[dcalc_arg.drvrPin()];
G_.coeffRef(node_count_ + drvr_idx, drvr_node) = 1.0;
G_.coeffRef(node_count_ + drvr_idx, node_count_ + drvr_idx) = -1.0;
// special sauce
G_.coeffRef(drvr_node, drvr_node) += 1e-6;
B_.coeffRef(drvr_node, drvr_idx) = 1.0;
stampDriver(dcalc_arg, drvr_idx);
const Parasitic *parasitic = dcalc_arg.parasitic();
if (!visited_parasitics.contains(parasitic)) {
stampResistors(parasitic);
stampCapacitors(parasitic, dcalc_arg, drvr_nets);
visited_parasitics.insert(parasitic);
}
}
if (debug_->check("ccs_dcalc", 3)) {
@ -645,6 +613,101 @@ PrimaDelayCalc::stampEqns()
}
}
void
PrimaDelayCalc::stampDriver(const ArcDcalcArg &dcalc_arg,
size_t drvr_idx)
{
size_t drvr_node = pin_node_map_[dcalc_arg.drvrPin()];
G_.coeffRef(node_count_ + drvr_idx, drvr_node) = 1.0;
G_.coeffRef(node_count_ + drvr_idx, node_count_ + drvr_idx) = -1.0;
// special sauce
G_.coeffRef(drvr_node, drvr_node) += 1e-6;
B_.coeffRef(drvr_node, drvr_idx) = 1.0;
}
void
PrimaDelayCalc::stampResistors(const Parasitic *parasitic)
{
for (ParasiticResistor *resistor : parasitics_->resistors(parasitic)) {
auto [node_idx1, exsits1] = nodeIndex(parasitics_->node1(resistor));
auto [node_idx2, exsits2] = nodeIndex(parasitics_->node2(resistor));
// Skip a resistor with a node left out of the network.
if (exsits1 && exsits2) {
float resistance = parasitics_->value(resistor);
// Skip a self loop / merged short (same index) or a non-positive (short)
// resistance; stamping 1/resistance would be infinite.
if (node_idx1 != node_idx2 && resistance > 0.0) {
stampConductance(node_idx1, node_idx2, 1.0 / resistance);
resistance_sum_ += resistance;
}
}
}
}
void
PrimaDelayCalc::stampCapacitors(const Parasitic *parasitic,
const ArcDcalcArg &dcalc_arg,
NetSet &drvr_nets)
{
const RiseFall *drvr_rf = dcalc_arg.drvrEdge();
bool includes_pin_caps = parasitics_->includesPinCaps(parasitic);
// Grounded capacitors.
for (ParasiticNode *node : parasitics_->nodes(parasitic)) {
if (!parasitics_->isExternal(node)) {
auto [node_idx, exists] = nodeIndex(node);
if (exists) {
double cap = parasitics_->nodeGndCap(node);
const Pin *pin = parasitics_->pin(node);
if (pin)
cap += pinCapacitance(pin, drvr_rf, includes_pin_caps);
stampCapacitance(node_idx, cap);
}
}
}
// Coupling capcacitors.
const Net *drvr_net = dcalc_arg.drvrNet(network_);
for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic)) {
ParasiticNode *node1 = parasitics_->node1(capacitor);
ParasiticNode *node2 = parasitics_->node2(capacitor);
float cap = parasitics_->value(capacitor);
const Net *net1 = node1 ? parasitics_->net(node1, network_) : nullptr;
const Net *net2 = node2 ? parasitics_->net(node2, network_) : nullptr;
if (net2 == drvr_net) {
std::swap(net1, net2);
std::swap(node1, node2);
}
auto [node_idx1, exists1] = nodeIndex(node1);
if (exists1) {
if (net2 && drvr_nets.contains(net2)) {
auto [node_idx2, exists2] = nodeIndex(node2);
if (exists2)
// Stamp half the capacitance because the coupled net will do the same.
stampCapacitance(node_idx1, node_idx2, cap * .5);
}
else
stampCapacitance(node_idx1, cap);
}
}
}
float
PrimaDelayCalc::pinCapacitance(const Pin *pin,
const RiseFall *rf,
bool includes_pin_caps)
{
Port *port = network_->port(pin);
LibertyPort *lib_port = network_->libertyPort(port);
const Sdc *sdc = scene_->sdc();
if (lib_port) {
if (!includes_pin_caps)
return sdc->pinCapacitance(pin, rf, scene_, min_max_);
}
else if (network_->isTopLevelPort(pin))
return sdc->portExtCap(port, rf, min_max_);
return 0.0;
}
// Grounded resistor.
void
PrimaDelayCalc::stampConductance(size_t n1,

View File

@ -133,12 +133,21 @@ protected:
void findLoads();
void findNodeCount();
void placeNode(ParasiticNode *node,
size_t index,
std::vector<ParasiticNode*> &queue);
size_t index);
void setOrder();
void initCeffIdrvr();
void setXinit();
std::pair<size_t, bool> nodeIndex(const ParasiticNode *node);
void stampEqns();
void stampDriver(const ArcDcalcArg &dcalc_arg,
size_t drvr_idx);
void stampResistors(const Parasitic *parasitic);
void stampCapacitors(const Parasitic *parasitic,
const ArcDcalcArg &dcalc_arg,
NetSet &drvr_nets);
float pinCapacitance(const Pin *pin,
const RiseFall *rf,
bool includes_pin_caps);
void stampConductance(size_t n1,
double g);
void stampConductance(size_t n1,
@ -149,7 +158,6 @@ protected:
void stampCapacitance(size_t n1,
size_t n2,
double cap);
float pinCapacitance(ParasiticNode *node);
void setPortCurrents();
void measureThresholds(double time);
double voltage(const Pin *pin);
@ -197,8 +205,6 @@ protected:
std::vector<OutputWaveforms*> output_waveforms_;
double resistance_sum_;
std::vector<double> node_capacitances_;
bool includes_pin_caps_;
float coupling_cap_multiplier_;
size_t node_count_; // Parasitic network node count