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();
|
||||
node_index_map_.clear();
|
||||
|
||||
for (ParasiticNode *node : parasitics_->nodes(parasitic_network_)) {
|
||||
if (!parasitics_->isExternal(node)) {
|
||||
size_t node_idx = node_index_map_.size();
|
||||
node_index_map_[node] = node_idx;
|
||||
const Pin *pin = parasitics_->pin(node);
|
||||
if (pin) {
|
||||
pin_node_map_[pin] = node_idx;
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "pin {} node {}",
|
||||
network_->pathName(pin), node_idx);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
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_)) {
|
||||
float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_;
|
||||
ParasiticNode *node1 = parasitics_->node1(capacitor);
|
||||
if (node1 && !parasitics_->isExternal(node1)) {
|
||||
size_t node_idx = node_index_map_[node1];
|
||||
node_capacitances_[node_idx] += cap;
|
||||
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)) {
|
||||
size_t node_idx = node_index_map_[node2];
|
||||
node_capacitances_[node_idx] += cap;
|
||||
auto itr = node_index_map_.find(node2);
|
||||
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
|
||||
|
|
@ -568,13 +612,17 @@ PrimaDelayCalc::stampEqns()
|
|||
|
||||
resistance_sum_ = 0.0;
|
||||
for (ParasiticResistor *resistor : parasitics_->resistors(parasitic_network_)) {
|
||||
ParasiticNode *node1 = parasitics_->node1(resistor);
|
||||
ParasiticNode *node2 = parasitics_->node2(resistor);
|
||||
// One commercial extractor creates resistors with identical from/to nodes.
|
||||
if (node1 != node2) {
|
||||
size_t node_idx1 = node_index_map_[node1];
|
||||
size_t node_idx2 = node_index_map_[node2];
|
||||
float resistance = parasitics_->value(resistor);
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,6 +132,9 @@ protected:
|
|||
void initSim();
|
||||
void findLoads();
|
||||
void findNodeCount();
|
||||
void placeNode(ParasiticNode *node,
|
||||
size_t index,
|
||||
std::vector<ParasiticNode*> &queue);
|
||||
void setOrder();
|
||||
void initCeffIdrvr();
|
||||
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
|
||||
power_json
|
||||
prima3
|
||||
prima_singular
|
||||
read_saif_null_instance
|
||||
report_checks_sorted
|
||||
report_checks_src_attr
|
||||
|
|
|
|||
Loading…
Reference in New Issue