From bb17250d94561e600e9cc6b20636af8c14b7e786 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 21 Apr 2024 18:13:54 +0200 Subject: [PATCH] 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. --- src/db/db/dbHierNetworkProcessor.cc | 6 +- src/db/db/dbHierNetworkProcessor.h | 17 +++ src/db/db/dbNetlistExtractor.cc | 95 +++++++++--- src/db/db/dbNetlistExtractor.h | 2 +- src/lvs/unit_tests/lvsSimpleTests.cc | 2 + testdata/lvs/soft_connect1a.cir | 13 ++ testdata/lvs/soft_connect1a.l2n | 218 +++++++++++++++++++++++++++ testdata/lvs/soft_connect1a.lvs | 92 +++++++++++ 8 files changed, 417 insertions(+), 28 deletions(-) create mode 100644 testdata/lvs/soft_connect1a.cir create mode 100644 testdata/lvs/soft_connect1a.l2n create mode 100644 testdata/lvs/soft_connect1a.lvs diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index bbb463faa..bd7dbd903 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1216,11 +1216,13 @@ struct cluster_building_receiver 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) { + bool soft = (g->second != 0); + typename std::map::iterator>::iterator icg = m_global_to_clusters.find (g->first); if (icg == m_global_to_clusters.end ()) { - if (g->second != 0) { + if (soft) { // soft connection to a new global cluster m_clusters.push_back (cluster_value ()); @@ -1240,7 +1242,7 @@ struct cluster_building_receiver } else if (ic->second != icg->second) { - if (g->second != 0) { + if (soft) { register_soft_connection (ic->second, icg->second, g->second); diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 08ec80718..ad97c7fad 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -513,6 +513,7 @@ public: typedef db::box_tree, local_cluster_box_convert > tree_type; typedef typename tree_type::touching_iterator touching_iterator; typedef typename tree_type::const_iterator const_iterator; + typedef typename tree_type::iterator iterator; /** * @brief Creates an empty collection @@ -566,6 +567,22 @@ public: 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 */ diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index dd4ac0c23..cfd253135 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -228,8 +228,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo const db::Cell &cell = mp_layout->cell (*cid); - const connected_clusters_type &clusters = mp_clusters->clusters_per_cell (*cid); - if (clusters.empty ()) { + connected_clusters_type &per_cell_clusters = mp_clusters->clusters_per_cell (*cid); + if (per_cell_clusters.empty ()) { bool any_good = false; @@ -251,7 +251,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo if (dm) { // This is a device abstract cell: // 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; } @@ -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 &lc = clusters.cluster_by_id (*c); - const connected_clusters_type::connections_type &cc = clusters.connections_for_cluster (*c); - const std::set &sc_up = clusters.upward_soft_connections (*c); - const std::set &sc_down = clusters.downward_soft_connections (*c); + const db::local_cluster &lc = per_cell_clusters.cluster_by_id (*c); + const connected_clusters_type::connections_type &cc = per_cell_clusters.connections_for_cluster (*c); + const std::set &sc_up = per_cell_clusters.upward_soft_connections (*c); + const std::set &sc_down = per_cell_clusters.downward_soft_connections (*c); if (cc.empty () && sc_up.empty () && sc_down.empty () && lc.empty ()) { // this is an entirely empty cluster so we skip it. // 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); // 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 (circuit, clusters, *c, net); + connect_devices (circuit, per_cell_clusters, *c, net); // collect labels to net names std::set net_names; - collect_labels (clusters, *c, net_names); + collect_labels (per_cell_clusters, *c, net_names); // add the global names as second priority 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. // 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 // 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) @@ -337,7 +337,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo 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 size_t pin_id = make_pin (circuit, net); c2p.insert (std::make_pair (*c, pin_id)); @@ -364,28 +364,73 @@ NetlistExtractor::assign_net_names (db::Net *net, const std::set &n net->set_name (nn); } +static void +collect_soft_connected_clusters (size_t from_id, const NetlistExtractor::connected_clusters_type &clusters, std::set &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 -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 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 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)) { + continue; + } + + db::properties_id_type pi = db::prop_id_from_attr (*a); + + 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) { + + if (j->first == m_terminal_annot_name_id.second) { + + size_t terminal_id = j->second.to (); + 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 (! db::is_prop_id_attr (*a)) { - continue; } - db::properties_id_type pi = db::prop_id_from_attr (*a); - - 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) { - if (j->first == m_terminal_annot_name_id.second) { - dm->set_cluster_id_for_terminal (j->second.to (), 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); } } diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index 18d7fed01..e06c29475 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -233,7 +233,7 @@ private: /** * @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); }; diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index ac141d640..1f06fefcf 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -305,6 +305,8 @@ TEST(40_DeviceExtractorErrors) TEST(50_BasicSoftConnection) { 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 diff --git a/testdata/lvs/soft_connect1a.cir b/testdata/lvs/soft_connect1a.cir new file mode 100644 index 000000000..61ac75956 --- /dev/null +++ b/testdata/lvs/soft_connect1a.cir @@ -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 diff --git a/testdata/lvs/soft_connect1a.l2n b/testdata/lvs/soft_connect1a.l2n new file mode 100644 index 000000000..c65a7d112 --- /dev/null +++ b/testdata/lvs/soft_connect1a.l2n @@ -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) + ) +) diff --git a/testdata/lvs/soft_connect1a.lvs b/testdata/lvs/soft_connect1a.lvs new file mode 100644 index 000000000..4063000dc --- /dev/null +++ b/testdata/lvs/soft_connect1a.lvs @@ -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 + +