Implemented a fix for issue #1691

The root cause was that the terminal identity could
not be derived in the presence of soft connections
inside the device abstract cell. The solution is to
join soft connected clusters in the device abstracts with
a warning.
This commit is contained in:
Matthias Koefferlein 2024-04-21 18:13:54 +02:00
parent 16ef23864c
commit bb17250d94
8 changed files with 417 additions and 28 deletions

View File

@ -1216,11 +1216,13 @@ struct cluster_building_receiver
db::Connectivity::global_nets_iterator ge = mp_conn->end_global_connections (p.first); db::Connectivity::global_nets_iterator ge = mp_conn->end_global_connections (p.first);
for (db::Connectivity::global_nets_iterator g = mp_conn->begin_global_connections (p.first); g != ge; ++g) { for (db::Connectivity::global_nets_iterator g = mp_conn->begin_global_connections (p.first); g != ge; ++g) {
bool soft = (g->second != 0);
typename std::map<size_t, typename std::list<cluster_value>::iterator>::iterator icg = m_global_to_clusters.find (g->first); typename std::map<size_t, typename std::list<cluster_value>::iterator>::iterator icg = m_global_to_clusters.find (g->first);
if (icg == m_global_to_clusters.end ()) { if (icg == m_global_to_clusters.end ()) {
if (g->second != 0) { if (soft) {
// soft connection to a new global cluster // soft connection to a new global cluster
m_clusters.push_back (cluster_value ()); m_clusters.push_back (cluster_value ());
@ -1240,7 +1242,7 @@ struct cluster_building_receiver
} else if (ic->second != icg->second) { } else if (ic->second != icg->second) {
if (g->second != 0) { if (soft) {
register_soft_connection (ic->second, icg->second, g->second); register_soft_connection (ic->second, icg->second, g->second);

View File

@ -513,6 +513,7 @@ public:
typedef db::box_tree<box_type, local_cluster<T>, local_cluster_box_convert<T> > tree_type; typedef db::box_tree<box_type, local_cluster<T>, local_cluster_box_convert<T> > tree_type;
typedef typename tree_type::touching_iterator touching_iterator; typedef typename tree_type::touching_iterator touching_iterator;
typedef typename tree_type::const_iterator const_iterator; typedef typename tree_type::const_iterator const_iterator;
typedef typename tree_type::iterator iterator;
/** /**
* @brief Creates an empty collection * @brief Creates an empty collection
@ -566,6 +567,22 @@ public:
return m_clusters.end (); return m_clusters.end ();
} }
/**
* @brief Gets the clusters (begin iterator)
*/
iterator begin ()
{
return m_clusters.begin ();
}
/**
* @brief Gets the clusters (end iterator)
*/
iterator end ()
{
return m_clusters.end ();
}
/** /**
* @brief Gets a value indicating whether the cluster set is empty * @brief Gets a value indicating whether the cluster set is empty
*/ */

View File

@ -228,8 +228,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
const db::Cell &cell = mp_layout->cell (*cid); const db::Cell &cell = mp_layout->cell (*cid);
const connected_clusters_type &clusters = mp_clusters->clusters_per_cell (*cid); connected_clusters_type &per_cell_clusters = mp_clusters->clusters_per_cell (*cid);
if (clusters.empty ()) { if (per_cell_clusters.empty ()) {
bool any_good = false; bool any_good = false;
@ -251,7 +251,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
if (dm) { if (dm) {
// This is a device abstract cell: // This is a device abstract cell:
// make the terminal to cluster ID connections for the device abstract from the device cells // make the terminal to cluster ID connections for the device abstract from the device cells
make_device_abstract_connections (dm, clusters); make_device_abstract_connections (dm, per_cell_clusters);
continue; continue;
} }
@ -284,12 +284,12 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
} }
for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { for (connected_clusters_type::all_iterator c = per_cell_clusters.begin_all (); ! c.at_end (); ++c) {
const db::local_cluster<db::NetShape> &lc = clusters.cluster_by_id (*c); const db::local_cluster<db::NetShape> &lc = per_cell_clusters.cluster_by_id (*c);
const connected_clusters_type::connections_type &cc = clusters.connections_for_cluster (*c); const connected_clusters_type::connections_type &cc = per_cell_clusters.connections_for_cluster (*c);
const std::set<size_t> &sc_up = clusters.upward_soft_connections (*c); const std::set<size_t> &sc_up = per_cell_clusters.upward_soft_connections (*c);
const std::set<size_t> &sc_down = clusters.downward_soft_connections (*c); const std::set<size_t> &sc_down = per_cell_clusters.downward_soft_connections (*c);
if (cc.empty () && sc_up.empty () && sc_down.empty () && lc.empty ()) { if (cc.empty () && sc_up.empty () && sc_down.empty () && lc.empty ()) {
// this is an entirely empty cluster so we skip it. // this is an entirely empty cluster so we skip it.
// Such clusters are left over when joining clusters. // Such clusters are left over when joining clusters.
@ -301,14 +301,14 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
circuit->add_net (net); circuit->add_net (net);
// make subcircuit connections (also make the subcircuits if required) from the connections of the clusters // make subcircuit connections (also make the subcircuits if required) from the connections of the clusters
make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell); make_and_connect_subcircuits (circuit, per_cell_clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell);
// connect devices // connect devices
connect_devices (circuit, clusters, *c, net); connect_devices (circuit, per_cell_clusters, *c, net);
// collect labels to net names // collect labels to net names
std::set<std::string> net_names; std::set<std::string> net_names;
collect_labels (clusters, *c, net_names); collect_labels (per_cell_clusters, *c, net_names);
// add the global names as second priority // add the global names as second priority
if (net_names.empty ()) { if (net_names.empty ()) {
@ -323,7 +323,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
// made to satisfy the subcircuit's pin, but not to make a physical connection. // made to satisfy the subcircuit's pin, but not to make a physical connection.
// Don't know whether this is a good idea, so this code is disabled for now. // Don't know whether this is a good idea, so this code is disabled for now.
if (net_names.empty () && clusters.is_dummy (*c) && net->subcircuit_pin_count () == 1) { if (net_names.empty () && per_cell_clusters.is_dummy (*c) && net->subcircuit_pin_count () == 1) {
// in the case of a dummy connection (partially connected subcircuits) create a // in the case of a dummy connection (partially connected subcircuits) create a
// new name indicating the subcircuit and the subcircuit net name - this makes subcircuit // new name indicating the subcircuit and the subcircuit net name - this makes subcircuit
// net names available (the net is pseudo-root inside in the subcircuit) // net names available (the net is pseudo-root inside in the subcircuit)
@ -337,7 +337,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
assign_net_names (net, net_names); assign_net_names (net, net_names);
if (! clusters.is_root (*c)) { if (! per_cell_clusters.is_root (*c)) {
// a non-root cluster makes a pin // a non-root cluster makes a pin
size_t pin_id = make_pin (circuit, net); size_t pin_id = make_pin (circuit, net);
c2p.insert (std::make_pair (*c, pin_id)); c2p.insert (std::make_pair (*c, pin_id));
@ -364,16 +364,44 @@ NetlistExtractor::assign_net_names (db::Net *net, const std::set<std::string> &n
net->set_name (nn); net->set_name (nn);
} }
static void
collect_soft_connected_clusters (size_t from_id, const NetlistExtractor::connected_clusters_type &clusters, std::set<size_t> &ids)
{
if (ids.find (from_id) != ids.end ()) {
return;
}
ids.insert (from_id);
auto upward = clusters.upward_soft_connections (from_id);
for (auto i = upward.begin (); i != upward.end (); ++i) {
collect_soft_connected_clusters (*i, clusters, ids);
}
auto downward = clusters.downward_soft_connections (from_id);
for (auto i = downward.begin (); i != downward.end (); ++i) {
collect_soft_connected_clusters (*i, clusters, ids);
}
}
void void
NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters) NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, connected_clusters_type &clusters)
{ {
// make the terminal to cluster ID connections for the device abstract from the device cells // make the terminal to cluster ID connections for the device abstract from the device cells
if (m_terminal_annot_name_id.first) { if (m_terminal_annot_name_id.first) {
for (connected_clusters_type::const_iterator dc = clusters.begin (); dc != clusters.end (); ++dc) { for (connected_clusters_type::iterator dc = clusters.begin (); dc != clusters.end (); ++dc) {
for (local_cluster_type::attr_iterator a = dc->begin_attr (); a != dc->end_attr (); ++a) { std::set<size_t> ids;
collect_soft_connected_clusters (dc->id (), clusters, ids);
for (auto id = ids.begin (); id != ids.end (); ++id) {
const local_cluster_type &lc = clusters.cluster_by_id (*id);
bool join = false;
for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) {
if (! db::is_prop_id_attr (*a)) { if (! db::is_prop_id_attr (*a)) {
continue; continue;
@ -383,9 +411,26 @@ NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, cons
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (pi); const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (pi);
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) { for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) {
if (j->first == m_terminal_annot_name_id.second) { if (j->first == m_terminal_annot_name_id.second) {
dm->set_cluster_id_for_terminal (j->second.to<size_t> (), dc->id ());
size_t terminal_id = j->second.to<size_t> ();
if (*id != dc->id ()) {
tl::warn << tl::sprintf (tl::to_string (tr ("Ignoring soft connection at device terminal %s for device %s")), dm->device_class ()->terminal_definition (terminal_id)->name (), dm->device_class ()->name ());
join = true;
} }
dm->set_cluster_id_for_terminal (terminal_id, dc->id ());
}
}
}
if (join) {
// copy the terminal attributes and shapes so we attach the terminal here in the device connection step
clusters.join_cluster_with (dc->id (), *id);
} }
} }

View File

@ -233,7 +233,7 @@ private:
/** /**
* @brief Makes the terminal to cluster ID connections of the device abstract * @brief Makes the terminal to cluster ID connections of the device abstract
*/ */
void make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters); void make_device_abstract_connections (db::DeviceAbstract *dm, connected_clusters_type &clusters);
}; };

View File

@ -305,6 +305,8 @@ TEST(40_DeviceExtractorErrors)
TEST(50_BasicSoftConnection) TEST(50_BasicSoftConnection)
{ {
run_test (_this, "soft_connect1", "soft_connect1.gds", true, false /*no LVS*/); run_test (_this, "soft_connect1", "soft_connect1.gds", true, false /*no LVS*/);
// issue #1691
run_test (_this, "soft_connect1a", "soft_connect1.gds", true, false /*no LVS*/);
} }
// No errors // No errors

13
testdata/lvs/soft_connect1a.cir vendored Normal file
View File

@ -0,0 +1,13 @@
* Extracted by KLayout
.SUBCKT TOP A Q VDD SUBSTRATE|VSS
X$1 SUBSTRATE|VSS VDD VDD \$1 Q SUBSTRATE|VSS INV
X$2 SUBSTRATE|VSS VDD VDD A \$1 SUBSTRATE|VSS INV
.ENDS TOP
.SUBCKT INV \$1 \$2 \$3 \$4 \$5 SUBSTRATE
M$1 \$2 \$4 \$5 \$3 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
M$2 \$1 \$4 \$5 SUBSTRATE NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
.ENDS INV

218
testdata/lvs/soft_connect1a.l2n vendored Normal file
View File

@ -0,0 +1,218 @@
#%l2n-klayout
W(TOP)
U(0.001)
L(l3 '1/0')
L(l4 '3/0')
L(l15 '3/1')
L(l8 '4/0')
L(l11 '5/0')
L(l12 '6/0')
L(l16 '6/1')
L(l13 '7/0')
L(l14 '8/0')
L(l17 '8/1')
L(l7)
L(l10)
L(l2)
L(l9)
L(l6)
C(l3 l3 l10)
C(l4 l4 l15 l11)
C(l15 l4 l15)
C(l8 l8 l12 l10 l2 l9 l6)
CS(l8 l10 l2 l9 l6)
C(l11 l4 l11 l12)
CS(l11 l4)
C(l12 l8 l11 l12 l16 l13)
C(l16 l12 l16)
C(l13 l12 l13 l14)
C(l14 l13 l14 l17)
C(l17 l14 l17)
C(l7 l7)
C(l10 l3 l8 l10)
CS(l10 l3)
C(l2 l8 l2)
C(l9 l8 l9)
C(l6 l8 l6)
G(l7 SUBSTRATE)
GS(l7 SUBSTRATE)
G(l9 SUBSTRATE)
GS(l9 SUBSTRATE)
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - VDD') C(TOP) Q('(0.6,3.95;0.6,4.85;4.5,4.85;4.5,3.95)'))
H(B('\tPartial net #2: TOP - $I4') C(TOP) Q('(5.1,3.95;5.1,4.85;9,4.85;9,3.95)'))
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - VSS') C(TOP) Q('(0.6,1.15;0.6,2.05;4.5,2.05;4.5,1.15)'))
H(B('\tPartial net #2: TOP - $I1') C(TOP) Q('(5.1,1.15;5.1,2.05;9,2.05;9,1.15)'))
K(PMOS MOS4)
K(NMOS MOS4)
D(D$PMOS PMOS
T(S
R(l2 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l2 (125 -475) (775 950))
)
T(B
R(l3 (-125 -475) (250 950))
)
)
D(D$NMOS NMOS
T(S
R(l6 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l6 (125 -475) (775 950))
)
T(B
R(l7 (-125 -475) (250 950))
)
)
X(INV
R((-1500 -800) (3000 4600))
N(1
R(l8 (290 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l13 (-305 -705) (250 250))
R(l13 (-250 150) (250 250))
R(l14 (-2025 -775) (3000 900))
R(l6 (-1375 -925) (775 950))
)
N(2
R(l8 (290 2490) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l13 (-305 -705) (250 250))
R(l13 (-250 150) (250 250))
R(l14 (-2025 -775) (3000 900))
R(l2 (-1375 -925) (775 950))
)
N(3
R(l3 (-1500 1800) (3000 2000))
)
N(4
R(l4 (-125 -250) (250 2500))
R(l4 (-250 -3050) (250 1600))
R(l4 (-250 1200) (250 1600))
)
N(5
R(l8 (-510 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l8 (-220 2180) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -3530) (360 2840))
R(l12 (-360 -2800) (360 760))
R(l12 (-360 2040) (360 760))
R(l2 (-680 -855) (775 950))
R(l6 (-775 -3750) (775 950))
)
N(6 I(SUBSTRATE))
P(1)
P(2)
P(3)
P(4)
P(5)
P(6 I(SUBSTRATE))
D(1 D$PMOS
Y(0 2800)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 5)
T(G 4)
T(D 2)
T(B 3)
)
D(2 D$NMOS
Y(0 0)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 5)
T(G 4)
T(D 1)
T(B 6)
)
)
X(TOP
R((600 800) (8880 4600))
N(1
R(l4 (2920 2600) (2880 400))
R(l11 (-300 -300) (200 200))
R(l12 (-300 -300) (690 400))
)
N(2 I(A)
R(l4 (6600 2600) (2880 400))
R(l15 (-2380 -200) (0 0))
)
N(3 I(Q)
R(l12 (1810 2600) (690 400))
R(l16 (-400 -200) (0 0))
)
N(4 I(VDD)
R(l3 (4000 3400) (1600 2000))
R(l3 (-5000 -2000) (1000 2000))
R(l3 (6400 -2000) (1000 2000))
R(l8 (-8000 -900) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (7200 200) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-7900 -350) (800 900))
R(l12 (6600 -900) (800 900))
R(l13 (-7900 -350) (200 200))
R(l13 (-200 -600) (200 200))
R(l13 (7200 200) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-8000 -350) (1000 900))
R(l14 (6400 -900) (1000 900))
R(l17 (-4800 -450) (0 0))
)
N(5 I('SUBSTRATE,VSS')
R(l8 (1000 1700) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (7200 200) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-7900 -350) (800 900))
R(l12 (6600 -900) (800 900))
R(l13 (-7900 -350) (200 200))
R(l13 (-200 -600) (200 200))
R(l13 (7200 200) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-8000 -350) (1000 900))
R(l14 (6400 -900) (1000 900))
R(l17 (-4800 -550) (0 0))
)
P(2 I(A))
P(3 I(Q))
P(4 I(VDD))
P(5 I('SUBSTRATE,VSS'))
X(1 INV Y(3000 1600)
P(0 5)
P(1 4)
P(2 4)
P(3 1)
P(4 3)
P(5 5)
)
X(2 INV Y(6600 1600)
P(0 5)
P(1 4)
P(2 4)
P(3 2)
P(4 1)
P(5 5)
)
)

92
testdata/lvs/soft_connect1a.lvs vendored Normal file
View File

@ -0,0 +1,92 @@
$lvs_test_source && source($lvs_test_source)
if $lvs_test_target_l2n
report_netlist($lvs_test_target_l2n)
else
report_netlist
end
writer = write_spice(true, false)
$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout")
deep
# Drawing layers
nwell = input(1, 0)
active = input(2, 0)
nplus = input(2, 1)
pplus = input(2, 2)
poly = input(3, 0)
poly_lbl = input(3, 1)
diff_cont = input(4, 0)
poly_cont = input(5, 0)
metal1 = input(6, 0)
metal1_lbl = input(6, 1)
via1 = input(7, 0)
metal2 = input(8, 0)
metal2_lbl = input(8, 1)
# Bulk layer for terminal provisioning
bulk = polygon_layer
psd = nil
nsd = nil
# Computed layers
active_in_nwell = active & nwell
pactive = active_in_nwell & pplus
ntie = active_in_nwell & nplus
pgate = pactive & poly
psd = pactive - pgate
active_outside_nwell = active - nwell
nactive = active_outside_nwell & nplus
ptie = active_outside_nwell & pplus
ngate = nactive & poly
nsd = nactive - ngate
# Device extraction
# PMOS transistor device extraction
extract_devices(mos4("PMOS"), { "SD" => psd, "G" => pgate, "W" => nwell,
"tS" => psd, "tD" => psd, "tG" => poly })
# NMOS transistor device extraction
extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk,
"tS" => nsd, "tD" => nsd, "tG" => poly })
# Define connectivity for netlist extraction
# Inter-layer
soft_connect(diff_cont, psd)
soft_connect(diff_cont, nsd)
soft_connect(diff_cont, ptie)
soft_connect(diff_cont, ntie)
soft_connect(ntie, nwell)
soft_connect(poly_cont, poly)
connect(diff_cont, metal1)
connect(poly_cont, metal1)
connect(metal1, via1)
connect(via1, metal2)
# attach labels
connect(poly, poly_lbl)
connect(metal1, metal1_lbl)
connect(metal2, metal2_lbl)
# Global
soft_connect_global(bulk, "SUBSTRATE")
soft_connect_global(ptie, "SUBSTRATE")
# Netlist section (NOTE: we only check log here)
netlist
netlist.simplify