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 + +