PrimaDelayCalc: handle degenerate parasitic networks (fixes STA-1752 "G matrix is singular") (#476)
* support for filter in get_scene/mode Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com> * fix singular G matrix issue for degenerate nets Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com> * fix the mistake on existing regression- was accidentally modified Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com> * make changes accounting for future SI support and address reviews Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com> * simplify comment Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com> * address feedbacks Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com> --------- Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
This commit is contained in:
parent
99eb809fff
commit
ef0b69091f
|
|
@ -467,35 +467,79 @@ PrimaDelayCalc::findNodeCount()
|
||||||
pin_node_map_.clear();
|
pin_node_map_.clear();
|
||||||
node_index_map_.clear();
|
node_index_map_.clear();
|
||||||
|
|
||||||
for (ParasiticNode *node : parasitics_->nodes(parasitic_network_)) {
|
// Collect the nodes that enter G by walking out from the drivers through
|
||||||
if (!parasitics_->isExternal(node)) {
|
// resistors. G is conductance-only, so a node with no resistive path to a
|
||||||
size_t node_idx = node_index_map_.size();
|
// driver has an all-zero row which is dropped to prevent singularity.
|
||||||
node_index_map_[node] = node_idx;
|
ParasiticNodeResistorMap resistor_map =
|
||||||
const Pin *pin = parasitics_->pin(node);
|
parasitics_->parasiticNodeResistorMap(parasitic_network_);
|
||||||
if (pin) {
|
std::vector<ParasiticNode *> queue;
|
||||||
pin_node_map_[pin] = node_idx;
|
for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) {
|
||||||
debugPrint(debug_, "ccs_dcalc", 1, "pin {} node {}",
|
const Pin *drvr_pin = (*dcalc_args_)[drvr_idx].drvrPin();
|
||||||
network_->pathName(pin), node_idx);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node);
|
|
||||||
node_capacitances_.push_back(cap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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_)) {
|
for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) {
|
||||||
float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_;
|
float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_;
|
||||||
ParasiticNode *node1 = parasitics_->node1(capacitor);
|
ParasiticNode *node1 = parasitics_->node1(capacitor);
|
||||||
if (node1 && !parasitics_->isExternal(node1)) {
|
if (node1 && !parasitics_->isExternal(node1)) {
|
||||||
size_t node_idx = node_index_map_[node1];
|
auto itr = node_index_map_.find(node1);
|
||||||
node_capacitances_[node_idx] += cap;
|
if (itr != node_index_map_.end())
|
||||||
|
node_capacitances_[itr->second] += cap;
|
||||||
}
|
}
|
||||||
ParasiticNode *node2 = parasitics_->node2(capacitor);
|
ParasiticNode *node2 = parasitics_->node2(capacitor);
|
||||||
if (node2 && !parasitics_->isExternal(node2)) {
|
if (node2 && !parasitics_->isExternal(node2)) {
|
||||||
size_t node_idx = node_index_map_[node2];
|
auto itr = node_index_map_.find(node2);
|
||||||
node_capacitances_[node_idx] += cap;
|
if (itr != node_index_map_.end())
|
||||||
|
node_capacitances_[itr->second] += cap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node_count_ = node_index_map_.size();
|
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.
|
||||||
|
void
|
||||||
|
PrimaDelayCalc::placeNode(ParasiticNode *node,
|
||||||
|
size_t index,
|
||||||
|
std::vector<ParasiticNode*> &queue)
|
||||||
|
{
|
||||||
|
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
|
float
|
||||||
|
|
@ -568,13 +612,17 @@ PrimaDelayCalc::stampEqns()
|
||||||
|
|
||||||
resistance_sum_ = 0.0;
|
resistance_sum_ = 0.0;
|
||||||
for (ParasiticResistor *resistor : parasitics_->resistors(parasitic_network_)) {
|
for (ParasiticResistor *resistor : parasitics_->resistors(parasitic_network_)) {
|
||||||
ParasiticNode *node1 = parasitics_->node1(resistor);
|
auto itr1 = node_index_map_.find(parasitics_->node1(resistor));
|
||||||
ParasiticNode *node2 = parasitics_->node2(resistor);
|
auto itr2 = node_index_map_.find(parasitics_->node2(resistor));
|
||||||
// One commercial extractor creates resistors with identical from/to nodes.
|
// Skip a resistor with a node left out of the network.
|
||||||
if (node1 != node2) {
|
if (itr1 == node_index_map_.end() || itr2 == node_index_map_.end())
|
||||||
size_t node_idx1 = node_index_map_[node1];
|
continue;
|
||||||
size_t node_idx2 = node_index_map_[node2];
|
size_t node_idx1 = itr1->second;
|
||||||
float resistance = parasitics_->value(resistor);
|
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);
|
stampConductance(node_idx1, node_idx2, 1.0 / resistance);
|
||||||
resistance_sum_ += resistance;
|
resistance_sum_ += resistance;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,9 @@ protected:
|
||||||
void initSim();
|
void initSim();
|
||||||
void findLoads();
|
void findLoads();
|
||||||
void findNodeCount();
|
void findNodeCount();
|
||||||
|
void placeNode(ParasiticNode *node,
|
||||||
|
size_t index,
|
||||||
|
std::vector<ParasiticNode*> &queue);
|
||||||
void setOrder();
|
void setOrder();
|
||||||
void initCeffIdrvr();
|
void initCeffIdrvr();
|
||||||
void setXinit();
|
void setXinit();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
Startpoint: r0 (rising edge-triggered flip-flop clocked by clk)
|
||||||
|
Endpoint: t0 (rising edge-triggered flip-flop clocked by clk)
|
||||||
|
Path Group: clk
|
||||||
|
Path Type: max
|
||||||
|
|
||||||
|
Delay Time Description
|
||||||
|
---------------------------------------------------------
|
||||||
|
0.00 0.00 clock clk (rise edge)
|
||||||
|
0.00 0.00 clock network delay (propagated)
|
||||||
|
0.00 0.00 ^ r0/CLK (DFFHQx4_ASAP7_75t_R)
|
||||||
|
68.24 68.24 ^ r0/Q (DFFHQx4_ASAP7_75t_R)
|
||||||
|
52.16 120.40 ^ u0/Y (BUFx2_ASAP7_75t_R)
|
||||||
|
18.40 138.80 ^ t0/D (DFFHQx4_ASAP7_75t_R)
|
||||||
|
138.80 data arrival time
|
||||||
|
|
||||||
|
500.00 500.00 clock clk (rise edge)
|
||||||
|
0.00 500.00 clock network delay (propagated)
|
||||||
|
0.00 500.00 clock reconvergence pessimism
|
||||||
|
500.00 ^ t0/CLK (DFFHQx4_ASAP7_75t_R)
|
||||||
|
-22.18 477.82 library setup time
|
||||||
|
477.82 data required time
|
||||||
|
---------------------------------------------------------
|
||||||
|
477.82 data required time
|
||||||
|
-138.80 data arrival time
|
||||||
|
---------------------------------------------------------
|
||||||
|
339.02 slack (MET)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
*SPEF "IEEE 1481-1998"
|
||||||
|
*DESIGN "top"
|
||||||
|
*DATE "2026"
|
||||||
|
*VENDOR "OpenSTA test"
|
||||||
|
*PROGRAM "hand written"
|
||||||
|
*VERSION "1.0.1c"
|
||||||
|
*DESIGN_FLOW "MISSING_NETS"
|
||||||
|
*DIVIDER /
|
||||||
|
*DELIMITER :
|
||||||
|
*BUS_DELIMITER [ ]
|
||||||
|
*T_UNIT 1.0 PS
|
||||||
|
*C_UNIT 1.0 FF
|
||||||
|
*R_UNIT 1.0 KOHM
|
||||||
|
*L_UNIT 1.0 UH
|
||||||
|
|
||||||
|
// Each buffer output net z<i> is degenerate two ways:
|
||||||
|
// - z<i>:2 z<i>:3 have ground cap but NO resistor (floating islands)
|
||||||
|
// - u<i>:Y -- z<i>:1 is a 0 KOHM resistor (an ideal short)
|
||||||
|
// Both used to make PrimaDelayCalc::primaReduce() factorize a singular G
|
||||||
|
// (STA-1752). node_count_ = 5 > prima_order_ (default 3) selects the
|
||||||
|
// primaReduce() path that factorizes the pure G matrix.
|
||||||
|
|
||||||
|
*D_NET z0 40.2
|
||||||
|
*CONN
|
||||||
|
*I u0:Y O
|
||||||
|
*I t0:D I *L .0086
|
||||||
|
*CAP
|
||||||
|
1 u0:Y 6.7
|
||||||
|
2 t0:D 6.7
|
||||||
|
3 z0:1 6.7
|
||||||
|
4 z0:2 6.7
|
||||||
|
5 z0:3 6.7
|
||||||
|
*RES
|
||||||
|
6 u0:Y z0:1 0
|
||||||
|
7 z0:1 t0:D 2.42
|
||||||
|
*END
|
||||||
|
|
||||||
|
*D_NET z1 40.2
|
||||||
|
*CONN
|
||||||
|
*I u1:Y O
|
||||||
|
*I t1:D I *L .0086
|
||||||
|
*CAP
|
||||||
|
1 u1:Y 6.7
|
||||||
|
2 t1:D 6.7
|
||||||
|
3 z1:1 6.7
|
||||||
|
4 z1:2 6.7
|
||||||
|
5 z1:3 6.7
|
||||||
|
*RES
|
||||||
|
6 u1:Y z1:1 0
|
||||||
|
7 z1:1 t1:D 2.42
|
||||||
|
*END
|
||||||
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Prima delay calc on degenerate parasitic networks (STA-1752 regression).
|
||||||
|
#
|
||||||
|
# Each buffer output net has both a floating (resistor-less) node and a
|
||||||
|
# zero-resistance short. Either one used to make PrimaDelayCalc's conductance
|
||||||
|
# matrix G singular, raising STA-1752 "G matrix is singular". Single threaded
|
||||||
|
# this surfaced as a Tcl error; multi threaded the error was thrown from a
|
||||||
|
# DispatchQueue worker and aborted with SIGABRT. findNodeCount() now drops
|
||||||
|
# isolated nodes and merges shorted nodes, so the delay is computed correctly.
|
||||||
|
#
|
||||||
|
# This test runs single threaded and checks the reported path. To exercise the
|
||||||
|
# historical multi-threaded crash path set STA_TEST_THREADS to the number of
|
||||||
|
# parallel buffers (2); the run must still complete without aborting. (A BFS
|
||||||
|
# level is dispatched to workers only when its vertex count >= the thread count,
|
||||||
|
# so more threads than buffers runs inline on the main thread.)
|
||||||
|
read_liberty asap7_small.lib.gz
|
||||||
|
read_verilog prima_singular.v
|
||||||
|
link_design top
|
||||||
|
create_clock -name clk -period 500 clk
|
||||||
|
set_input_delay -clock clk 1 [list in0 in1]
|
||||||
|
set_input_transition 10 [list clk in0 in1]
|
||||||
|
set_propagated_clock clk
|
||||||
|
read_spef prima_singular.spef
|
||||||
|
sta::set_delay_calculator prima
|
||||||
|
if { [info exists ::env(STA_TEST_THREADS)] } {
|
||||||
|
sta::set_thread_count $::env(STA_TEST_THREADS)
|
||||||
|
} else {
|
||||||
|
sta::set_thread_count 1
|
||||||
|
}
|
||||||
|
report_checks -group_path_count 1
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
module top (clk, in0, in1, out0, out1);
|
||||||
|
input clk, in0, in1;
|
||||||
|
output out0, out1;
|
||||||
|
wire q0, q1, z0, z1;
|
||||||
|
DFFHQx4_ASAP7_75t_R r0 (.D(in0), .CLK(clk), .Q(q0));
|
||||||
|
BUFx2_ASAP7_75t_R u0 (.A(q0), .Y(z0));
|
||||||
|
DFFHQx4_ASAP7_75t_R t0 (.D(z0), .CLK(clk), .Q(out0));
|
||||||
|
DFFHQx4_ASAP7_75t_R r1 (.D(in1), .CLK(clk), .Q(q1));
|
||||||
|
BUFx2_ASAP7_75t_R u1 (.A(q1), .Y(z1));
|
||||||
|
DFFHQx4_ASAP7_75t_R t1 (.D(z1), .CLK(clk), .Q(out1));
|
||||||
|
endmodule
|
||||||
|
|
@ -161,6 +161,7 @@ record_public_tests {
|
||||||
path_group_names
|
path_group_names
|
||||||
power_json
|
power_json
|
||||||
prima3
|
prima3
|
||||||
|
prima_singular
|
||||||
read_saif_null_instance
|
read_saif_null_instance
|
||||||
report_checks_sorted
|
report_checks_sorted
|
||||||
report_checks_src_attr
|
report_checks_src_attr
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue