diff --git a/samples/lvs/RINGO.cir b/samples/lvs/RINGO.cir new file mode 100644 index 000000000..e97b148a1 --- /dev/null +++ b/samples/lvs/RINGO.cir @@ -0,0 +1,27 @@ + +.SUBCKT RINGO VSS VDD FB ENABLE OUT +X$1 VDD VSS 1 FB ENABLE ND2X1 +X$2 VDD VSS 2 1 INVX1 +X$3 VDD VSS 3 2 INVX1 +X$4 VDD VSS 4 3 INVX1 +X$5 VDD VSS 5 4 INVX1 +X$6 VDD VSS 6 5 INVX1 +X$7 VDD VSS 7 6 INVX1 +X$8 VDD VSS 8 7 INVX1 +X$9 VDD VSS 9 8 INVX1 +X$10 VDD VSS 10 9 INVX1 +X$11 VDD VSS FB 10 INVX1 +X$12 VDD VSS OUT FB INVX1 +.ENDS RINGO + +.SUBCKT ND2X1 VDD VSS OUT B A +M$1 OUT A VDD VDD LVPMOS L=0.25U W=1.5U +M$2 VDD B OUT VDD LVPMOS L=0.25U W=1.5U +M$3 VSS A 1 VSS LVNMOS L=0.25U W=0.95U +M$4 1 B OUT VSS LVNMOS L=0.25U W=0.95U +.ENDS ND2X1 + +.SUBCKT INVX1 VDD VSS OUT IN +M$1 VDD IN OUT VDD LVPMOS L=0.25U W=1.5U +M$2 VSS IN OUT VSS LVNMOS L=0.25U W=0.95U +.ENDS INVX1 diff --git a/samples/lvs/ringo.gds b/samples/lvs/ringo.gds index fa116f14d..c29e3d5a6 100644 Binary files a/samples/lvs/ringo.gds and b/samples/lvs/ringo.gds differ diff --git a/samples/lvs/schematic.cir b/samples/lvs/schematic.cir deleted file mode 100644 index 13d6d09f5..000000000 --- a/samples/lvs/schematic.cir +++ /dev/null @@ -1,27 +0,0 @@ - -.SUBCKT RINGO VSS VDD FB ENABLE OUT -X$1 VDD 1 VSS VDD FB ENABLE VSS ND2X1 -X$2 VDD 2 VSS VDD 1 VSS INVX1 -X$3 VDD 3 VSS VDD 2 VSS INVX1 -X$4 VDD 4 VSS VDD 3 VSS INVX1 -X$5 VDD 5 VSS VDD 4 VSS INVX1 -X$6 VDD 6 VSS VDD 5 VSS INVX1 -X$7 VDD 7 VSS VDD 6 VSS INVX1 -X$8 VDD 8 VSS VDD 7 VSS INVX1 -X$9 VDD 9 VSS VDD 8 VSS INVX1 -X$10 VDD 10 VSS VDD 9 VSS INVX1 -X$11 VDD FB VSS VDD 10 VSS INVX1 -X$12 VDD OUT VSS VDD FB VSS INVX1 -.ENDS RINGO - -.SUBCKT ND2X1 VDD OUT VSS NWELL B A BULK -M$1 OUT A VDD NWELL PMOS L=0.25U W=1.5U -M$2 VDD B OUT NWELL PMOS L=0.25U W=1.5U -M$3 VSS A 1 BULK NMOS L=0.25U W=0.95U -M$4 1 B OUT BULK NMOS L=0.25U W=0.95U -.ENDS ND2X1 - -.SUBCKT INVX1 VDD OUT VSS NWELL IN BULK -M$1 VDD IN OUT NWELL PMOS L=0.25U W=1.5U -M$2 VSS IN OUT BULK NMOS L=0.25U W=0.95U -.ENDS INVX1 diff --git a/samples/lvs/si4all.lvs b/samples/lvs/si4all.lvs new file mode 100644 index 000000000..992369916 --- /dev/null +++ b/samples/lvs/si4all.lvs @@ -0,0 +1,128 @@ + +# Hierarchical mode +deep +# Print details +verbose + +# Output generation (dialog only) +report_lvs + +# Enable this to produce a L2N database +# report_netlist("extracted.l2n") + +# True to write the extracted netlist +if false + + # true: use net names instead of numbers + # false: use numbers for nets + spice_with_net_names = true + # true: put in comments with details + # false: no comments + spice_with_comments = false + + # Extracted netlist + target_netlist(File.join(File.dirname(File.absolute_path(source.path || ".")), source.cell_name + "_extracted.cir"), write_spice(spice_with_net_names, spice_with_comments), "Extracted by KLayout on : #{Time.now.strftime("%d/%m/%Y %H:%M")}") + +end + +# Specify the schematic netlist +# (looks for a file called .cir where +# is the current cell name). The file is looked up relative to +# the layout file name. +schematic(File.join(File.dirname(File.absolute_path(source.path || ".")), source.cell_name + ".cir")) + +# layers definitions +######################## +nwell = input(1, 0) +diff = input(2, 0) +pplus = input(3, 0) +nplus = input(4, 0) +poly = input(5, 0) +thickox = input(6, 0) +polyres = input(7, 0) +contact = input(8, 0) +metal1 = input(9, 0) +via = input(10, 0) +metal2 = input(11, 0) +pad = input(12, 0) +border = input(13, 0) + +# Special layer for bulk terminals + +bulk = make_layer + +# Computed layers + +diff_in_nwell = diff & nwell +pdiff = diff_in_nwell - nplus +ntie = diff_in_nwell & nplus +pgate = pdiff & poly +psd = pdiff - pgate +hv_pgate = pgate & thickox +lv_pgate = pgate - hv_pgate +hv_psd = psd & thickox +lv_psd = psd - thickox + +diff_outside_nwell = diff - nwell +ndiff = diff_outside_nwell - pplus +ptie = diff_outside_nwell & pplus +ngate = ndiff & poly +nsd = ndiff - ngate +hv_ngate = ngate & thickox +lv_ngate = ngate - hv_ngate +hv_nsd = nsd & thickox +lv_nsd = nsd - thickox + +# PMOS transistor device extraction + +hvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVPMOS") +extract_devices(hvpmos_ex, { "SD" => psd, "G" => hv_pgate, "P" => poly, "W" => nwell }) + +lvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVPMOS") +extract_devices(lvpmos_ex, { "SD" => psd, "G" => lv_pgate, "P" => poly, "W" => nwell }) + +# NMOS transistor device extraction + +lvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVNMOS") +extract_devices(lvnmos_ex, { "SD" => nsd, "G" => lv_ngate, "P" => poly, "W" => bulk }) + +hvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVNMOS") +extract_devices(hvnmos_ex, { "SD" => nsd, "G" => hv_ngate, "P" => poly, "W" => bulk }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(contact, ntie) +connect(contact, ptie) +connect(nwell, ntie) +connect(psd, contact) +connect(nsd, contact) +connect(poly, contact) +connect(contact, metal1) +connect(metal1, via) +connect(via, metal2) + +# Make "must-connect" connections between NWELL and VDD and BULK and VSS +connect_explicit(["NWELL", "VDD"]) +connect_explicit(["BULK", "VSS"]) + +# Global connections +connect_global(ptie, "BULK") +connect_global(bulk, "BULK") + +# Actually performs the extraction +netlist + +# Flatten cells which are present in one netlist only +align + +# Simplication of the netlist +netlist.simplify + +# LVS compare +if compare + puts "Congratulations! Netlists match." +else + puts "LVS ERROR: netlists do not match!" +end + diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 8f0e76770..b6aac64f3 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -45,6 +45,7 @@ SOURCES = \ dbLayoutDiff.cc \ dbLayoutQuery.cc \ dbLayoutStateModel.cc \ + dbLayoutToNetlistSoftConnections.cc \ dbLayoutUtils.cc \ dbLibrary.cc \ dbLibraryManager.cc \ @@ -270,6 +271,7 @@ HEADERS = \ dbLayoutQuery.h \ dbLayoutStateModel.h \ dbLayoutToNetlistEnums.h \ + dbLayoutToNetlistSoftConnections.h \ dbLayoutUtils.h \ dbLibrary.h \ dbLibraryManager.h \ diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 066d8eeb0..bbb463faa 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -113,11 +113,20 @@ Connectivity::Connectivity (edge_connectivity_type ec) // .. nothing yet .. } +void +Connectivity::soft_connect (unsigned int la, unsigned int lb) +{ + m_connected [la][lb] = -1; + m_connected [lb][la] = 1; + m_all_layers.insert (la); + m_all_layers.insert (lb); +} + void Connectivity::connect (unsigned int la, unsigned int lb) { - m_connected [la].insert (lb); - m_connected [lb].insert (la); + m_connected [la][lb] = 0; + m_connected [lb][la] = 0; m_all_layers.insert (la); m_all_layers.insert (lb); } @@ -125,7 +134,7 @@ Connectivity::connect (unsigned int la, unsigned int lb) void Connectivity::connect (unsigned int l) { - m_connected [l].insert (l); + m_connected [l][l] = 0; m_all_layers.insert (l); } @@ -141,6 +150,12 @@ Connectivity::connect (const db::DeepLayer &la, const db::DeepLayer &lb) connect (la.layer (), lb.layer ()); } +void +Connectivity::soft_connect (const db::DeepLayer &la, const db::DeepLayer &lb) +{ + soft_connect (la.layer (), lb.layer ()); +} + Connectivity::global_nets_type s_empty_global_nets; Connectivity::global_nets_iterator @@ -169,7 +184,16 @@ size_t Connectivity::connect_global (unsigned int l, const std::string &gn) { size_t id = global_net_id (gn); - m_global_connections [l].insert (id); + m_global_connections [l][id] = 0; + m_all_layers.insert (l); + return id; +} + +size_t +Connectivity::soft_connect_global (unsigned int l, const std::string &gn) +{ + size_t id = global_net_id (gn); + m_global_connections [l][id] = -1; m_all_layers.insert (l); return id; } @@ -180,6 +204,12 @@ Connectivity::connect_global (const db::DeepLayer &l, const std::string &gn) return connect_global (l.layer (), gn); } +size_t +Connectivity::soft_connect_global (const db::DeepLayer &l, const std::string &gn) +{ + return soft_connect_global (l.layer (), gn); +} + const std::string & Connectivity::global_net_name (size_t id) const { @@ -208,13 +238,13 @@ Connectivity::global_nets () const return m_global_net_names.size (); } -Connectivity::layer_iterator +Connectivity::all_layer_iterator Connectivity::begin_layers () const { return m_all_layers.begin (); } -Connectivity::layer_iterator +Connectivity::all_layer_iterator Connectivity::end_layers () const { return m_all_layers.end (); @@ -306,13 +336,19 @@ interaction_test (const db::Edge &a, const db::Edge &b, const db::unit_trans } template -bool Connectivity::interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans) const +bool Connectivity::interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans, int &soft) const { std::map::const_iterator i = m_connected.find (la); - if (i == m_connected.end () || i->second.find (lb) == i->second.end ()) { + if (i == m_connected.end ()) { return false; } else { - return interaction_test (a, b, trans, m_ec); + auto t = i->second.find (lb); + if (t == i->second.end () || ! interaction_test (a, b, trans, m_ec)) { + return false; + } else { + soft = t->second; + return true; + } } } @@ -321,7 +357,7 @@ bool Connectivity::interacts (const std::set &la, const std::set::const_iterator i = la.begin (); i != la.end (); ++i) { db::Connectivity::layer_iterator je = end_connected (*i); for (db::Connectivity::layer_iterator j = begin_connected (*i); j != je; ++j) { - if (lb.find (*j) != lb.end ()) { + if (lb.find (j->first) != lb.end ()) { return true; } } @@ -335,7 +371,7 @@ bool Connectivity::interact (const db::Cell &a, const db::Cell &b) const for (std::map::const_iterator i = m_connected.begin (); i != m_connected.end (); ++i) { if (! a.bbox (i->first).empty ()) { for (layers_type::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { - if (! b.bbox (*j).empty ()) { + if (! b.bbox (j->first).empty ()) { return true; } } @@ -353,7 +389,7 @@ bool Connectivity::interact (const db::Cell &a, const T &ta, const db::Cell &b, if (! ba.empty ()) { ba.transform (ta); for (layers_type::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { - db::Box bb = b.bbox (*j); + db::Box bb = b.bbox (j->first); if (! bb.empty () && bb.transformed (tb).touches (ba)) { return true; } @@ -365,12 +401,12 @@ bool Connectivity::interact (const db::Cell &a, const T &ta, const db::Cell &b, } // explicit instantiations -template DB_PUBLIC bool Connectivity::interacts (const db::NetShape &a, unsigned int la, const db::NetShape &b, unsigned int lb, const db::UnitTrans &trans) const; -template DB_PUBLIC bool Connectivity::interacts (const db::NetShape &a, unsigned int la, const db::NetShape &b, unsigned int lb, const db::ICplxTrans &trans) const; -template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::UnitTrans &trans) const; -template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::ICplxTrans &trans) const; -template DB_PUBLIC bool Connectivity::interacts (const db::Edge &a, unsigned int la, const db::Edge &b, unsigned int lb, const db::UnitTrans &trans) const; -template DB_PUBLIC bool Connectivity::interacts (const db::Edge &a, unsigned int la, const db::Edge &b, unsigned int lb, const db::ICplxTrans &trans) const; +template DB_PUBLIC bool Connectivity::interacts (const db::NetShape &a, unsigned int la, const db::NetShape &b, unsigned int lb, const db::UnitTrans &trans, int &soft) const; +template DB_PUBLIC bool Connectivity::interacts (const db::NetShape &a, unsigned int la, const db::NetShape &b, unsigned int lb, const db::ICplxTrans &trans, int &soft) const; +template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::UnitTrans &trans, int &soft) const; +template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::ICplxTrans &trans, int &soft) const; +template DB_PUBLIC bool Connectivity::interacts (const db::Edge &a, unsigned int la, const db::Edge &b, unsigned int lb, const db::UnitTrans &trans, int &soft) const; +template DB_PUBLIC bool Connectivity::interacts (const db::Edge &a, unsigned int la, const db::Edge &b, unsigned int lb, const db::ICplxTrans &trans, int &soft) const; template DB_PUBLIC bool Connectivity::interact (const db::Cell &a, const db::ICplxTrans &ta, const db::Cell &b, const db::ICplxTrans &tb) const; // ------------------------------------------------------------------------------ @@ -489,15 +525,21 @@ public: typedef typename local_cluster::box_type box_type; hnp_interaction_receiver (const Connectivity &conn, const db::ICplxTrans &trans) - : mp_conn (&conn), m_any (false), m_trans (trans) + : mp_conn (&conn), m_any (false), m_soft_mode (0), m_trans (trans) { // .. nothing yet .. } void add (const T *s1, unsigned int l1, const T *s2, unsigned int l2) { - if (mp_conn->interacts (*s1, l1, *s2, l2, m_trans)) { - m_any = true; + int soft = 0; + if (mp_conn->interacts (*s1, l1, *s2, l2, m_trans, soft)) { + if (soft == 0 || (m_soft_mode != 0 && m_soft_mode != soft)) { + m_soft_mode = 0; + m_any = true; + } else { + m_soft_mode = soft; + } } } @@ -506,9 +548,15 @@ public: return m_any; } + int soft_mode () const + { + return m_soft_mode; + } + private: const Connectivity *mp_conn; bool m_any; + int m_soft_mode; db::ICplxTrans m_trans; }; @@ -559,7 +607,7 @@ local_cluster::interacts (const db::Cell &cell, const db::ICplxTrans &trans, Connectivity::layer_iterator le = conn.end_connected (s->first); for (Connectivity::layer_iterator l = conn.begin_connected (s->first); l != le; ++l) { - box += cell.bbox (*l); + box += cell.bbox (l->first); } if (! box.empty () && ! s->second.begin_touching (box.transformed (trans), bc).at_end ()) { @@ -573,7 +621,7 @@ local_cluster::interacts (const db::Cell &cell, const db::ICplxTrans &trans, template bool -local_cluster::interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const +local_cluster::interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft) const { db::box_convert bc; @@ -632,7 +680,13 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans } hnp_interaction_receiver rec (conn, trans); - return ! scanner.process (rec, 1 /*==touching*/, bc, bc_t); + + if (! scanner.process (rec, 1 /*==touching*/, bc, bc_t) || rec.soft_mode () != 0) { + soft = rec.soft_mode (); + return true; + } else { + return false; + } } template @@ -755,6 +809,8 @@ void local_clusters::clear () m_clusters.clear (); m_bbox = box_type (); m_next_dummy_id = 0; + m_soft_connections.clear (); + m_soft_connections_rev.clear (); } template @@ -792,6 +848,9 @@ local_clusters::remove_cluster (typename local_cluster::id_type id) local_cluster *to_delete = const_cast *> (& m_clusters.objects ().item (id - 1)); to_delete->clear (); m_needs_update = true; + + // take care of soft connections + remove_soft_connections_for (id); } template @@ -799,18 +858,33 @@ void local_clusters::join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id) { tl_assert (id > 0); - if (with_id == 0 || with_id > m_clusters.size () || id > m_clusters.size ()) { - return; + + if (with_id > 0 && with_id <= m_clusters.size () && id <= m_clusters.size ()) { + + // TODO: this const_cast is required. But we know what we're doing ... + local_cluster *with = const_cast *> (& m_clusters.objects ().item (with_id - 1)); + local_cluster *first = const_cast *> (& m_clusters.objects ().item (id - 1)); + first->join_with (*with); + + // NOTE: we cannot really delete a cluster as this would shift the indexes. So + // we just clear them. + with->clear (); + } - // TODO: this const_cast is required. But we know what we're doing ... - local_cluster *with = const_cast *> (& m_clusters.objects ().item (with_id - 1)); - local_cluster *first = const_cast *> (& m_clusters.objects ().item (id - 1)); - first->join_with (*with); + // take care of soft connections - // NOTE: we cannot really delete a cluster as this would shift the indexes. So - // we just clear them. - with->clear (); + std::set conn_down = downward_soft_connections (with_id); + std::set conn_up = upward_soft_connections (with_id); + + remove_soft_connections_for (with_id); + + for (auto i = conn_down.begin (); i != conn_down.end (); ++i) { + make_soft_connection (id, *i); + } + for (auto i = conn_up.begin (); i != conn_up.end (); ++i) { + make_soft_connection (*i, id); + } m_needs_update = true; } @@ -825,6 +899,77 @@ local_clusters::insert () return i.operator-> (); } +template +void +local_clusters::make_soft_connection (typename local_cluster::id_type a, typename local_cluster::id_type b) +{ + if (a == b) { + return; + } + + // NOTE: antiparallel connections are allowed. We will eliminate them later + + m_soft_connections [a].insert (b); + m_soft_connections_rev [b].insert (a); +} + +template +const std::set & +local_clusters::downward_soft_connections (typename local_cluster::id_type id) const +{ + static std::set empty; + + auto sc = m_soft_connections.find (id); + if (sc != m_soft_connections.end ()) { + return sc->second; + } else { + return empty; + } +} + +template +const std::set & +local_clusters::upward_soft_connections (typename local_cluster::id_type id) const +{ + static std::set empty; + + auto sc = m_soft_connections_rev.find (id); + if (sc != m_soft_connections_rev.end ()) { + return sc->second; + } else { + return empty; + } +} + +static void +remove_id_from_map (std::map > &fwd, std::map > &rev, size_t id) +{ + auto sc = fwd.find (id); + if (sc != fwd.end ()) { + + for (auto i = sc->second.begin (); i != sc->second.end (); ++i) { + auto sc_rev = rev.find (*i); + if (sc_rev != rev.end ()) { + sc_rev->second.erase (id); + if (sc_rev->second.empty ()) { + rev.erase (*i); + } + } + } + + fwd.erase (sc); + + } +} + +template +void +local_clusters::remove_soft_connections_for (typename local_cluster::id_type id) +{ + remove_id_from_map (m_soft_connections, m_soft_connections_rev, id); + remove_id_from_map (m_soft_connections_rev, m_soft_connections, id); +} + template void local_clusters::ensure_sorted () @@ -857,6 +1002,8 @@ local_clusters::mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpo db::mem_stat (stat, purpose, cat, m_bbox, true, (void *) this); db::mem_stat (stat, purpose, cat, m_clusters, true, (void *) this); db::mem_stat (stat, purpose, cat, m_next_dummy_id, true, (void *) this); + db::mem_stat (stat, purpose, cat, m_soft_connections, true, (void *) this); + db::mem_stat (stat, purpose, cat, m_soft_connections_rev, true, (void *) this); } namespace @@ -888,6 +1035,8 @@ struct cluster_building_receiver void generate_clusters (local_clusters &clusters) { + std::map::id_type> in_to_out; + // build the resulting clusters for (typename std::list::const_iterator c = m_clusters.begin (); c != m_clusters.end (); ++c) { @@ -898,6 +1047,8 @@ struct cluster_building_receiver cluster->add_attr (s->second.second); } + in_to_out.insert (std::make_pair (c.operator-> (), cluster->id ())); + std::set global_nets = c->second; // Add the global nets we derive from the attribute equivalence (join_nets of labelled vs. @@ -926,6 +1077,20 @@ struct cluster_building_receiver cluster->set_global_nets (global_nets); } + + for (auto sc = m_soft_connections.begin (); sc != m_soft_connections.end (); ++sc) { + + if (sc->second >= 0) { + continue; + } + + auto c1 = in_to_out.find (sc->first.first); + auto c2 = in_to_out.find (sc->first.second); + tl_assert (c1 != in_to_out.end () && c2 != in_to_out.end ()); + + clusters.make_soft_connection (c1->second, c2->second); + + } } void add (const T *s1, std::pair p1, const T *s2, std::pair p2) @@ -933,7 +1098,8 @@ struct cluster_building_receiver if (m_separate_attributes && p1.second != p2.second) { return; } - if (! mp_conn->interacts (*s1, p1.first, *s2, p2.first)) { + int soft = 0; + if (! mp_conn->interacts (*s1, p1.first, *s2, p2.first, soft)) { return; } @@ -944,34 +1110,88 @@ struct cluster_building_receiver if (ic2 == m_shape_to_clusters.end ()) { - m_clusters.push_back (cluster_value ()); - typename std::list::iterator c = --m_clusters.end (); - c->first.push_back (std::make_pair (s1, p1)); - c->first.push_back (std::make_pair (s2, p2)); + if (soft != 0) { - m_shape_to_clusters.insert (std::make_pair (s1, c)); - m_shape_to_clusters.insert (std::make_pair (s2, c)); + m_clusters.push_back (cluster_value ()); + typename std::list::iterator c1 = --m_clusters.end (); + m_clusters.push_back (cluster_value ()); + typename std::list::iterator c2 = --m_clusters.end (); + c1->first.push_back (std::make_pair (s1, p1)); + c2->first.push_back (std::make_pair (s2, p2)); + + m_shape_to_clusters.insert (std::make_pair (s1, c1)); + m_shape_to_clusters.insert (std::make_pair (s2, c2)); + + register_soft_connection (c1, c2, soft); + + } else { + + m_clusters.push_back (cluster_value ()); + typename std::list::iterator c = --m_clusters.end (); + c->first.push_back (std::make_pair (s1, p1)); + c->first.push_back (std::make_pair (s2, p2)); + + m_shape_to_clusters.insert (std::make_pair (s1, c)); + m_shape_to_clusters.insert (std::make_pair (s2, c)); + + } } else { - ic2->second->first.push_back (std::make_pair (s1, p1)); - m_shape_to_clusters.insert (std::make_pair (s1, ic2->second)); + if (soft != 0) { + + m_clusters.push_back (cluster_value ()); + typename std::list::iterator c1 = --m_clusters.end (); + + c1->first.push_back (std::make_pair (s1, p1)); + m_shape_to_clusters.insert (std::make_pair (s1, c1)); + + register_soft_connection (c1, ic2->second, soft); + + } else { + + ic2->second->first.push_back (std::make_pair (s1, p1)); + m_shape_to_clusters.insert (std::make_pair (s1, ic2->second)); + + } } } else if (ic2 == m_shape_to_clusters.end ()) { - ic1->second->first.push_back (std::make_pair (s2, p2)); - m_shape_to_clusters.insert (std::make_pair (s2, ic1->second)); + if (soft != 0) { + + m_clusters.push_back (cluster_value ()); + typename std::list::iterator c2 = --m_clusters.end (); + + c2->first.push_back (std::make_pair (s2, p2)); + m_shape_to_clusters.insert (std::make_pair (s2, c2)); + + register_soft_connection (ic1->second, c2, soft); + + } else { + + ic1->second->first.push_back (std::make_pair (s2, p2)); + m_shape_to_clusters.insert (std::make_pair (s2, ic1->second)); + + } } else if (ic1->second != ic2->second) { - // join clusters: use the larger one as the target + if (soft != 0) { + + register_soft_connection (ic1->second, ic2->second, soft); - if (ic1->second->first.size () < ic2->second->first.size ()) { - join (ic2->second, ic1->second); } else { - join (ic1->second, ic2->second); + + // join clusters: use the larger one as the target + + if (ic1->second->first.size () < ic2->second->first.size ()) { + join (ic2->second, ic1->second); + } else { + join (ic1->second, ic2->second); + } + } } @@ -996,20 +1216,43 @@ 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) { - typename std::map::iterator>::iterator icg = m_global_to_clusters.find (*g); + typename std::map::iterator>::iterator icg = m_global_to_clusters.find (g->first); if (icg == m_global_to_clusters.end ()) { - ic->second->second.insert (*g); - m_global_to_clusters.insert (std::make_pair (*g, ic->second)); + if (g->second != 0) { + + // soft connection to a new global cluster + m_clusters.push_back (cluster_value ()); + typename std::list::iterator cg = --m_clusters.end (); + + cg->second.insert (g->first); + m_global_to_clusters.insert (std::make_pair (g->first, cg)); + + register_soft_connection (ic->second, cg, g->second); + + } else { + + ic->second->second.insert (g->first); + m_global_to_clusters.insert (std::make_pair (g->first, ic->second)); + + } } else if (ic->second != icg->second) { - // join clusters - if (ic->second->first.size () < icg->second->first.size ()) { - join (icg->second, ic->second); + if (g->second != 0) { + + register_soft_connection (ic->second, icg->second, g->second); + } else { - join (ic->second, icg->second); + + // join clusters + if (ic->second->first.size () < icg->second->first.size ()) { + join (icg->second, ic->second); + } else { + join (ic->second, icg->second); + } + } } @@ -1022,12 +1265,17 @@ private: std::map::iterator> m_shape_to_clusters; std::map::iterator> m_global_to_clusters; std::list m_clusters; + std::map, int> m_soft_connections; std::map > m_global_nets_by_attribute_cluster; const tl::equivalence_clusters *mp_attr_equivalence; bool m_separate_attributes; void join (typename std::list::iterator ic1, typename std::list::iterator ic2) { + if (ic1 == ic2) { + return; + } + ic1->first.insert (ic1->first.end (), ic2->first.begin (), ic2->first.end ()); ic1->second.insert (ic2->second.begin (), ic2->second.end ()); @@ -1039,6 +1287,49 @@ private: } m_clusters.erase (ic2); + + // remap soft connections: remove soft connections between ic1 and ic2 and + // remap ic2 connections to ic1 as first + + auto i2 = m_soft_connections.lower_bound (std::make_pair (ic2.operator-> (), (const cluster_value *) 0)); + for (auto i = i2; i != m_soft_connections.end () && i->first.first == ic2.operator-> (); ++i) { + m_soft_connections.erase (std::make_pair (i->first.second, i->first.first)); + } + + for (auto i = i2; i != m_soft_connections.end () && i->first.first == ic2.operator-> (); ++i) { + if (i->first.second != ic1.operator-> ()) { + register_soft_connection (ic1.operator-> (), i->first.second, i->second); + } + } + + auto i2_from = i2; + for ( ; i2 != m_soft_connections.end () && i2->first.first == ic2.operator-> (); ++i2) + ; + m_soft_connections.erase (i2_from, i2); + } + + void register_soft_connection (typename std::list::iterator ic1, typename std::list::iterator ic2, int soft) + { + register_soft_connection (ic1.operator-> (), ic2.operator-> (), soft); + } + + void register_soft_connection (const cluster_value *c1, const cluster_value *c2, int soft) + { + auto i1 = m_soft_connections.find (std::make_pair (c1, c2)); + auto i2 = m_soft_connections.find (std::make_pair (c2, c1)); + + if (i1 != m_soft_connections.end () && i1->second != 0 && i1->second != soft) { + i1->second = 0; + } + if (i2 != m_soft_connections.end () && i2->second != 0 && i2->second != -soft) { + i2->second = 0; + } + + if (i1 == m_soft_connections.end ()) { + tl_assert (i2 == m_soft_connections.end ()); + m_soft_connections.insert (std::make_pair (std::make_pair (c1, c2), soft)); + m_soft_connections.insert (std::make_pair (std::make_pair (c2, c1), -soft)); + } } }; @@ -1110,7 +1401,7 @@ local_clusters::build_clusters (const db::Cell &cell, const db::Connectivity attr_accessor attr; db::ShapeIterator::flags_type shape_flags = get_shape_flags () (); - for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + for (db::Connectivity::all_layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { const db::Shapes &shapes = cell.shapes (*l); for (db::Shapes::shape_iterator s = shapes.begin (shape_flags); ! s.at_end (); ++s) { bs.insert (heap (*s), std::make_pair (*l, attr (*s))); @@ -1131,6 +1422,7 @@ void local_clusters::apply_attr_equivalences (const tl::equivalence_clusters &attr_equivalence) { // identify the layout clusters joined into one attribute cluster and join them + // NOTE: soft connections are not supported here, but that is a deprecated mechanism anyway. std::map::cluster_id_type, std::set > c2c; @@ -1395,17 +1687,18 @@ public: struct ClusterInstanceInteraction { - ClusterInstanceInteraction (size_t _cluster_id, const ClusterInstance &_other_ci) - : cluster_id (_cluster_id), other_ci (_other_ci) + ClusterInstanceInteraction (size_t _cluster_id, const ClusterInstance &_other_ci, int _soft) + : cluster_id (_cluster_id), other_ci (_other_ci), soft (_soft) { } bool operator== (const ClusterInstanceInteraction &other) const { - return cluster_id == other.cluster_id && other_ci == other.other_ci; + return cluster_id == other.cluster_id && other_ci == other.other_ci && soft == other.soft; } size_t cluster_id; ClusterInstance other_ci; + int soft; }; /** @@ -1459,7 +1752,7 @@ public: { db::ICplxTrans t; - std::list > ic; + std::list ic; consider_instance_pair (box_type::world (), *i1, t, db::CellInstArray::iterator (), *i2, t, db::CellInstArray::iterator (), ic); connect_clusters (ic); @@ -1496,21 +1789,50 @@ public: void finish_cluster_to_instance_interactions () { for (typename std::list::const_iterator ii = m_ci_interactions.begin (); ii != m_ci_interactions.end (); ++ii) { - mp_tree->propagate_cluster_inst (*mp_layout, *mp_cell, ii->other_ci, mp_cell->cell_index (), false); + + // NOTE: soft connections make a dummy cluster at the level of our instance. This allows registering a soft connection to it. + mp_tree->propagate_cluster_inst (*mp_layout, *mp_cell, ii->other_ci, mp_cell->cell_index (), ii->soft != 0 ? true /*with self*/ : false /*without self*/); + } for (typename std::list::const_iterator ii = m_ci_interactions.begin (); ii != m_ci_interactions.end (); ++ii) { - id_type other = mp_cell_clusters->find_cluster_with_connection (ii->other_ci); - if (other > 0) { + if (ii->soft != 0) { - // we found a child cluster that connects two clusters on our own level: - // we must join them into one, but not now. We're still iterating and - // would invalidate the box trees. So keep this now and combine the clusters later. - mark_to_join (other, ii->cluster_id); + id_type other = mp_cell_clusters->find_cluster_with_connection (ii->other_ci); + tl_assert (other > 0); // because we used "with self" in "propagate_cluster_inst" + + register_soft_connection (ii->cluster_id, other, ii->soft); } else { - mp_cell_clusters->add_connection (ii->cluster_id, ii->other_ci); + + id_type other = mp_cell_clusters->find_cluster_with_connection (ii->other_ci); + if (other > 0) { + + // we found a child cluster that connects two clusters on our own level: + // we must join them into one, but not now. We're still iterating and + // would invalidate the box trees. So keep this now and combine the clusters later. + mark_to_join (other, ii->cluster_id); + + } else { + mp_cell_clusters->add_connection (ii->cluster_id, ii->other_ci); + } + + } + + } + + for (auto sc = m_soft_connections.begin (); sc != m_soft_connections.end (); ++sc) { + + if (sc->second == 0) { + // dropped soft connection + continue; + } + + if (sc->second > 0) { + mp_cell_clusters->make_soft_connection (sc->first.second, sc->first.first); + } else { + mp_cell_clusters->make_soft_connection (sc->first.first, sc->first.second); } } @@ -1529,6 +1851,7 @@ public: } } + } // needs explicit implementation because we have two base classes: @@ -1547,8 +1870,9 @@ private: typedef std::list > join_set_list; std::map m_cm2join_map; join_set_list m_cm2join_sets; + std::map, int> m_soft_connections; std::list m_ci_interactions; - instance_interaction_cache, std::list > > m_interaction_cache_for_clusters; + instance_interaction_cache, std::list > m_interaction_cache_for_clusters; size_t m_cluster_cache_misses, m_cluster_cache_hits; typename hier_clusters::instance_interaction_cache_type *mp_instance_interaction_cache; bool m_separate_attributes; @@ -1624,10 +1948,10 @@ private: interacting_clusters = *cached; for (cluster_instance_pair_list_type::iterator i = interacting_clusters.begin (); i != interacting_clusters.end (); ++i) { // translate the property IDs - i->first.set_inst_prop_id (i1.prop_id ()); - i->first.transform (i1t); - i->second.set_inst_prop_id (i2.prop_id ()); - i->second.transform (i2t); + i->a.set_inst_prop_id (i1.prop_id ()); + i->a.transform (i1t); + i->b.set_inst_prop_id (i2.prop_id ()); + i->b.transform (i2t); } return; @@ -1643,13 +1967,13 @@ private: // reject the pair bool any = false; - for (db::Connectivity::layer_iterator la = mp_conn->begin_layers (); la != mp_conn->end_layers () && ! any; ++la) { + for (db::Connectivity::all_layer_iterator la = mp_conn->begin_layers (); la != mp_conn->end_layers () && ! any; ++la) { db::box_convert bca (*mp_layout, *la); box_type bb1 = i1.cell_inst ().bbox (bca).transformed (t1); if (! bb1.empty ()) { db::Connectivity::layer_iterator lbe = mp_conn->end_connected (*la); for (db::Connectivity::layer_iterator lb = mp_conn->begin_connected (*la); lb != lbe && ! any; ++lb) { - db::box_convert bcb (*mp_layout, *lb); + db::box_convert bcb (*mp_layout, lb->first); box_type bb2 = i2.cell_inst ().bbox (bcb).transformed (t2); any = bb1.touches (bb2); } @@ -1694,23 +2018,23 @@ private: box_type common12 = ib1 & ib2 & common; if (! common12.empty ()) { - const std::list > &i2i_interactions = compute_instance_interactions (common12, i1.cell_index (), tt1, i2.cell_index (), tt2); + const std::list &i2i_interactions = compute_instance_interactions (common12, i1.cell_index (), tt1, i2.cell_index (), tt2); - for (std::list >::const_iterator ii = i2i_interactions.begin (); ii != i2i_interactions.end (); ++ii) { - ClusterInstance k1 (ii->first, i1.cell_index (), i1t, i1.prop_id ()); - ClusterInstance k2 (ii->second, i2.cell_index (), i2t, i2.prop_id ()); - interacting_clusters.push_back (std::make_pair (k1, k2)); + for (std::list::const_iterator ii = i2i_interactions.begin (); ii != i2i_interactions.end (); ++ii) { + ClusterInstance k1 (ii->a, i1.cell_index (), i1t, i1.prop_id ()); + ClusterInstance k2 (ii->b, i2.cell_index (), i2t, i2.prop_id ()); + interacting_clusters.push_back (ClusterInstancePair (k1, k2, ii->soft)); } // dive into cell of ii2 const db::Cell &cell2 = mp_layout->cell (i2.cell_index ()); for (db::Cell::touching_iterator jj2 = cell2.begin_touching (common12.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { - std::list > ii_interactions; + std::list ii_interactions; consider_instance_pair (common12, i1, t1, ii1, *jj2, tt2, db::CellInstArray::iterator (), ii_interactions); - for (std::list >::iterator i = ii_interactions.begin (); i != ii_interactions.end (); ++i) { - propagate_cluster_inst (i->second, i2.cell_index (), i2t, i2.prop_id ()); + for (std::list::iterator i = ii_interactions.begin (); i != ii_interactions.end (); ++i) { + propagate_cluster_inst (i->b, i2.cell_index (), i2t, i2.prop_id ()); } ii_interactions.unique (); @@ -1722,11 +2046,11 @@ private: const db::Cell &cell1 = mp_layout->cell (i1.cell_index ()); for (db::Cell::touching_iterator jj1 = cell1.begin_touching (common12.transformed (tt1.inverted ())); ! jj1.at_end (); ++jj1) { - std::list > ii_interactions; + std::list ii_interactions; consider_instance_pair (common12, *jj1, tt1, db::CellInstArray::iterator (), i2, t2, ii2, ii_interactions); - for (std::list >::iterator i = ii_interactions.begin (); i != ii_interactions.end (); ++i) { - propagate_cluster_inst (i->first, i1.cell_index (), i1t, i1.prop_id ()); + for (std::list::iterator i = ii_interactions.begin (); i != ii_interactions.end (); ++i) { + propagate_cluster_inst (i->a, i1.cell_index (), i1t, i1.prop_id ()); } ii_interactions.unique (); @@ -1751,7 +2075,7 @@ private: // remove duplicates (after doing a quick unique before) // NOTE: duplicates may happen due to manifold child/child interactions which boil down to // identical parent cluster interactions. - std::vector > sorted_interactions; + std::vector sorted_interactions; sorted_interactions.reserve (interacting_clusters.size ()); sorted_interactions.insert (sorted_interactions.end (), interacting_clusters.begin (), interacting_clusters.end ()); interacting_clusters.clear (); @@ -1765,9 +2089,9 @@ private: // normalize transformations for cache db::ICplxTrans i1ti = i1t.inverted (), i2ti = i2t.inverted (); - for (std::vector >::iterator i = sorted_interactions.begin (); i != sorted_interactions.end (); ++i) { - i->first.transform (i1ti); - i->second.transform (i2ti); + for (std::vector::iterator i = sorted_interactions.begin (); i != sorted_interactions.end (); ++i) { + i->a.transform (i1ti); + i->b.transform (i2ti); } cluster_instance_pair_list_type &cached = mp_instance_interaction_cache->insert (i1.cell_index (), i2.cell_index (), ii_key); @@ -1779,13 +2103,13 @@ private: /** * @brief Computes a list of interacting clusters for two instances */ - const std::list > & + const std::list & compute_instance_interactions (const box_type &common, db::cell_index_type ci1, const db::ICplxTrans &t1, db::cell_index_type ci2, const db::ICplxTrans &t2) { if (is_breakout_cell (mp_breakout_cells, ci1) || is_breakout_cell (mp_breakout_cells, ci2)) { - static const std::list > empty; + static const std::list empty; return empty; } @@ -1797,7 +2121,7 @@ private: interaction_key_for_clusters ikey (t1i, t21, common2); - const std::list > *ici = m_interaction_cache_for_clusters.find (ci1, ci2, ikey); + const std::list *ici = m_interaction_cache_for_clusters.find (ci1, ci2, ikey); if (ici) { return *ici; @@ -1809,7 +2133,7 @@ private: const db::local_clusters &cl1 = mp_tree->clusters_per_cell (ci1); const db::local_clusters &cl2 = mp_tree->clusters_per_cell (ci2); - std::list > &new_interactions = m_interaction_cache_for_clusters.insert (ci1, ci2, ikey); + std::list &new_interactions = m_interaction_cache_for_clusters.insert (ci1, ci2, ikey); db::ICplxTrans t12 = t2i * t1; for (typename db::local_clusters::touching_iterator i = cl1.begin_touching (common2.transformed (t21)); ! i.at_end (); ++i) { @@ -1822,8 +2146,9 @@ private: box_type bc2 = (common2 & i->bbox ().transformed (t12)); for (typename db::local_clusters::touching_iterator j = cl2.begin_touching (bc2); ! j.at_end (); ++j) { - if ((! m_separate_attributes || i->same_attrs (*j)) && i->interacts (*j, t21, *mp_conn)) { - new_interactions.push_back (std::make_pair (i->id (), j->id ())); + int soft = 0; + if ((! m_separate_attributes || i->same_attrs (*j)) && i->interacts (*j, t21, *mp_conn, soft)) { + new_interactions.push_back (ClusterIDPair (i->id (), j->id (), soft)); } } @@ -1875,22 +2200,22 @@ private: cluster_instance_pair_list_type interacting_clusters; box_type common = (ib & ib2); - const std::list > &i2i_interactions = compute_instance_interactions (common, i.cell_index (), tt, i.cell_index (), tt2); - for (std::list >::const_iterator ii = i2i_interactions.begin (); ii != i2i_interactions.end (); ++ii) { - ClusterInstance k1 (ii->first, i.cell_index (), tt, i.prop_id ()); - ClusterInstance k2 (ii->second, i.cell_index (), tt2, i.prop_id ()); - interacting_clusters.push_back (std::make_pair (k1, k2)); + const std::list &i2i_interactions = compute_instance_interactions (common, i.cell_index (), tt, i.cell_index (), tt2); + for (std::list::const_iterator ii = i2i_interactions.begin (); ii != i2i_interactions.end (); ++ii) { + ClusterInstance k1 (ii->a, i.cell_index (), tt, i.prop_id ()); + ClusterInstance k2 (ii->b, i.cell_index (), tt2, i.prop_id ()); + interacting_clusters.push_back (ClusterInstancePair (k1, k2, ii->soft)); } for (db::Cell::touching_iterator jj2 = cell.begin_touching (common.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) { db::ICplxTrans t; - std::list > ii_interactions; + std::list ii_interactions; consider_instance_pair (common, i, t, ii, *jj2, tt2, db::CellInstArray::iterator (), ii_interactions); - for (std::list >::iterator ii = ii_interactions.begin (); ii != ii_interactions.end (); ++ii) { - propagate_cluster_inst (ii->second, i.cell_index (), tt2, i.prop_id ()); + for (std::list::iterator ii = ii_interactions.begin (); ii != ii_interactions.end (); ++ii) { + propagate_cluster_inst (ii->b, i.cell_index (), tt2, i.prop_id ()); } interacting_clusters.splice (interacting_clusters.end (), ii_interactions, ii_interactions.begin (), ii_interactions.end ()); @@ -1911,6 +2236,7 @@ private: } } + } /** @@ -1937,7 +2263,7 @@ private: return; } - std::vector > c2i_interactions; + std::vector c2i_interactions; for (db::CellInstArray::iterator ii2 = i2.begin_touching ((b1 & b2).transformed (t2.inverted ()), mp_layout); ! ii2.at_end (); ++ii2) { @@ -1950,9 +2276,9 @@ private: c2i_interactions.clear (); compute_cluster_instance_interactions (c1, i2.cell_index (), tt2, c2i_interactions); - for (std::vector >::const_iterator ii = c2i_interactions.begin (); ii != c2i_interactions.end (); ++ii) { - ClusterInstance k (ii->second, i2.cell_index (), i2t, i2.prop_id ()); - interactions_out.push_back (ClusterInstanceInteraction (ii->first, k)); + for (std::vector::const_iterator ii = c2i_interactions.begin (); ii != c2i_interactions.end (); ++ii) { + ClusterInstance k (ii->b, i2.cell_index (), i2t, i2.prop_id ()); + interactions_out.push_back (ClusterInstanceInteraction (ii->a, k, ii->soft)); } // dive into cell of ii2 @@ -1984,7 +2310,7 @@ private: void compute_cluster_instance_interactions (const local_cluster &c1, db::cell_index_type ci2, const db::ICplxTrans &t2, - std::vector > &interactions) + std::vector &interactions) { if (is_breakout_cell (mp_breakout_cells, ci2)) { return; @@ -1994,8 +2320,9 @@ private: for (typename db::local_clusters::touching_iterator j = cl2.begin_touching (c1.bbox ().transformed (t2.inverted ())); ! j.at_end (); ++j) { - if ((! m_separate_attributes || c1.same_attrs (*j)) && c1.interacts (*j, t2, *mp_conn)) { - interactions.push_back (std::make_pair (c1.id (), j->id ())); + int soft = 0; + if ((! m_separate_attributes || c1.same_attrs (*j)) && c1.interacts (*j, t2, *mp_conn, soft)) { + interactions.push_back (ClusterIDPair (c1.id (), j->id (), soft)); } } @@ -2098,8 +2425,8 @@ private: { for (cluster_instance_pair_list_type::const_iterator ic = interacting_clusters.begin (); ic != interacting_clusters.end (); ++ic) { - const ClusterInstance &k1 = ic->first; - const ClusterInstance &k2 = ic->second; + const ClusterInstance &k1 = ic->a; + const ClusterInstance &k2 = ic->b; // Note: "with_self" is false as we're going to create a connected cluster anyway id_type x1 = mp_tree->propagate_cluster_inst (*mp_layout, *mp_cell, k1, mp_cell->cell_index (), false); @@ -2109,34 +2436,102 @@ private: if (x2 == 0) { - id_type connector = mp_cell_clusters->insert_dummy (); - mp_cell_clusters->add_connection (connector, k1); - mp_cell_clusters->add_connection (connector, k2); + if (ic->soft != 0) { + + id_type ca = mp_cell_clusters->insert_dummy (); + id_type cb = mp_cell_clusters->insert_dummy (); + mp_cell_clusters->add_connection (ca, k1); + mp_cell_clusters->add_connection (cb, k2); + + register_soft_connection (ca, cb, ic->soft); + + } else { + + id_type connector = mp_cell_clusters->insert_dummy (); + mp_cell_clusters->add_connection (connector, k1); + mp_cell_clusters->add_connection (connector, k2); + + } } else { - mp_cell_clusters->add_connection (x2, k1); + + if (ic->soft != 0) { + + id_type ca = mp_cell_clusters->insert_dummy (); + mp_cell_clusters->add_connection (ca, k1); + + register_soft_connection (ca, x2, ic->soft); + + } else { + + mp_cell_clusters->add_connection (x2, k1); + + } } } else if (x2 == 0) { - mp_cell_clusters->add_connection (x1, k2); + if (ic->soft != 0) { + + id_type cb = mp_cell_clusters->insert_dummy (); + mp_cell_clusters->add_connection (cb, k2); + + register_soft_connection (x1, cb, ic->soft); + + } else { + + mp_cell_clusters->add_connection (x1, k2); + + } } else if (x1 != x2) { + int soft = ic->soft; + // for instance-to-instance interactions the number of connections is more important for the // cost of the join operation: make the one with more connections the target // TODO: this will be SLOW for STL's not providing a fast size() if (mp_cell_clusters->connections_for_cluster (x1).size () < mp_cell_clusters->connections_for_cluster (x2).size ()) { std::swap (x1, x2); + soft = -soft; } - mp_cell_clusters->join_cluster_with (x1, x2); - mp_cell_clusters->remove_cluster (x2); + if (soft != 0) { + + register_soft_connection (x1, x2, soft); + + } else { + + mp_cell_clusters->join_cluster_with (x1, x2); + mp_cell_clusters->remove_cluster (x2); + + } } } } + + /** + * @brief Registers a soft connection for clusters a and b + */ + void register_soft_connection (id_type a, id_type b, int soft) + { + std::pair key (a < b ? a : b, a < b ? b : a); + if (!(a < b)) { + soft = -soft; + } + + auto sc = m_soft_connections.find (key); + if (sc != m_soft_connections.end ()) { + if (sc->second != 0 && sc->second != soft) { + sc->second = 0; // turn into hard connection + mark_to_join (a, b); + } + } else { + m_soft_connections [key] = soft; + } + } }; template @@ -2186,6 +2581,10 @@ hier_clusters::propagate_cluster_inst (const db::Layout &layout, const db::Ce if (child_cc.is_root (id)) { std::set > seen; // to avoid duplicate connections + if (! with_self) { + // don't include the instance we came up with initially + seen.insert (std::make_pair (cell.cell_index (), ci)); + } const db::Cell &child_cell = layout.cell (ci.inst_cell_index ()); for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { @@ -2196,7 +2595,7 @@ hier_clusters::propagate_cluster_inst (const db::Layout &layout, const db::Ce for (db::CellInstArray::iterator pii = child_inst.begin (); ! pii.at_end (); ++pii) { ClusterInstance ci2 (id, child_inst.cell_index (), child_inst.complex_trans (*pii), child_inst.prop_id ()); - if ((with_self || cell.cell_index () != pi->parent_cell_index () || ci != ci2) && seen.find (std::make_pair (pi->parent_cell_index (), ci2)) == seen.end ()) { + if (seen.find (std::make_pair (pi->parent_cell_index (), ci2)) == seen.end ()) { size_t id_dummy; @@ -2559,7 +2958,6 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c if (! i->has_instance ()) { local.join_cluster_with (gcid, i->id ()); - local.remove_cluster (i->id ()); } else { @@ -2570,7 +2968,6 @@ hier_clusters::build_hier_connections (cell_clusters_box_converter &cbc, c // shouldn't happen, but duplicate instances may trigger this } else if (other_id) { local.join_cluster_with (gcid, other_id); - local.remove_cluster (other_id); } else { local.add_connection (gcid, *i); } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 13c557d64..08ec80718 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -60,9 +60,11 @@ class DeepLayer; class DB_PUBLIC Connectivity { public: - typedef std::set layers_type; + typedef std::set all_layers_type; + typedef all_layers_type::const_iterator all_layer_iterator; + typedef std::map layers_type; typedef layers_type::const_iterator layer_iterator; - typedef std::set global_nets_type; + typedef std::map global_nets_type; typedef global_nets_type::const_iterator global_nets_iterator; /** @@ -101,11 +103,26 @@ public: */ void connect (unsigned int la, unsigned int lb); + /** + * @brief Adds inter-layer connectivity of the soft type + * + * Soft connections are directed and are reported during "interacts" + * by the "soft" output argument. "la" is the "upper" layer and "lb" is the lower layer. + */ + void soft_connect (unsigned int la, unsigned int lb); + /** * @brief Adds a connection to a global net */ size_t connect_global (unsigned int l, const std::string &gn); + /** + * @brief Adds a soft connection to a global net + * + * The global net is always the "lower" layer. + */ + size_t soft_connect_global (unsigned int l, const std::string &gn); + /** * @brief Adds intra-layer connectivity for layer l * This is a convenience method that takes a db::DeepLayer object. @@ -120,11 +137,28 @@ public: */ void connect (const db::DeepLayer &la, const db::DeepLayer &lb); + /** + * @brief Adds inter-layer connectivity + * This is a convenience method that takes a db::DeepLayer object. + * It is assumed that all those layers originate from the same deep shape store. + * + * Soft connections are directed and are reported during "interacts" + * by the "soft" output argument. "la" is the "upper" layer and "lb" is the lower layer. + */ + void soft_connect (const db::DeepLayer &la, const db::DeepLayer &lb); + /** * @brief Adds a connection to a global net */ size_t connect_global (const db::DeepLayer &la, const std::string &gn); + /** + * @brief Adds a soft connection to a global net + * + * The global net is always the "lower" layer. + */ + size_t soft_connect_global (const db::DeepLayer &la, const std::string &gn); + /** * @brief Gets the global net name per ID */ @@ -143,15 +177,19 @@ public: /** * @brief Begin iterator for the layers involved */ - layer_iterator begin_layers () const; + all_layer_iterator begin_layers () const; /** * @brief End iterator for the layers involved */ - layer_iterator end_layers () const; + all_layer_iterator end_layers () const; /** * @brief Begin iterator for the layers connected to a specific layer + * + * The iterator returned is over a map of target layers and soft mode + * (an int, being 0 for a hard connection, +1 for an upward soft connection + * and -1 for a downward soft connection). */ layer_iterator begin_connected (unsigned int layer) const; @@ -162,6 +200,10 @@ public: /** * @brief Begin iterator for the global connections for a specific layer + * + * The iterator returned is over a map of global net ID and soft mode + * (an int, being 0 for a hard connection, +1 for an upward soft connection + * and -1 for a downward soft connection). */ global_nets_iterator begin_global_connections (unsigned int layer) const; @@ -175,17 +217,21 @@ public: * * This method accepts a transformation. This transformation is applied * to the b shape before checking against a. + * + * The "soft" output argument will deliver the soft mode that applies + * to the connection - 0: hard connection, -1: a is the lower layer, +1: a is + * the upper layer. */ template - bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans) const; + bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans, int &soft) const; /** * @brief Returns true, if the given shapes on the given layers interact */ template - bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb) const + bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb, int &soft) const { - return interacts (a, la, b, lb, UnitTrans ()); + return interacts (a, la, b, lb, UnitTrans (), soft); } /** @@ -195,17 +241,21 @@ public: /** * @brief Returns true, if two cells basically (without considering transformation) interact + * + * This is a pretty basic check based on the cell's bounding boxes */ bool interact (const db::Cell &a, const db::Cell &b) const; /** * @brief Returns true, if two cells with the given transformations interact + * + * This is a pretty basic check based on the cell's bounding boxes */ template bool interact (const db::Cell &a, const T &ta, const db::Cell &b, const T &tb) const; private: - layers_type m_all_layers; + all_layers_type m_all_layers; std::map m_connected; std::vector m_global_net_names; std::map m_global_connections; @@ -277,7 +327,7 @@ public: * "trans" is the transformation which is applied to the other cluster before * the test. */ - bool interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const; + bool interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft) const; /** * @brief Tests whether this cluster interacts with the given cell @@ -288,7 +338,7 @@ public: bool interacts (const db::Cell &cell, const db::ICplxTrans &trans, const Connectivity &conn) const; /** - * @brief Gets the bounding box of this cluster + * @brief Gets the bounding box ofF this cluster */ const box_type &bbox () const { @@ -572,6 +622,21 @@ public: return id > m_clusters.size (); } + /** + * @brief Makes a soft connection between clusters a and b (a: upper, b: lower) + */ + void make_soft_connection (typename local_cluster::id_type a, typename local_cluster::id_type b); + + /** + * @brief Get the downward soft connections for a given cluster + */ + const std::set &downward_soft_connections (typename local_cluster::id_type id) const; + + /** + * @brief Get the upward soft connections for a given cluster + */ + const std::set &upward_soft_connections (typename local_cluster::id_type id) const; + /** * @brief Gets the number of clusters */ @@ -592,8 +657,11 @@ private: box_type m_bbox; tree_type m_clusters; size_t m_next_dummy_id; + std::map > m_soft_connections; + std::map > m_soft_connections_rev; void apply_attr_equivalences (const tl::equivalence_clusters &attr_equivalence); + void remove_soft_connections_for (typename local_cluster::id_type id); }; /** @@ -794,7 +862,61 @@ private: size_t m_id; }; -typedef std::list > cluster_instance_pair_list_type; +struct ClusterInstancePair +{ + ClusterInstancePair (const ClusterInstance &_a, const ClusterInstance &_b, int _soft) + : a (_a), b (_b), soft (_soft) + { } + + bool operator== (const ClusterInstancePair &other) const + { + return a == other.a && b == other.b && soft == other.soft; + } + + bool operator< (const ClusterInstancePair &other) const + { + if (!(a == other.a)) { + return a < other.a; + } + if (!(b == other.b)) { + return b < other.b; + } + return soft < other.soft; + } + + ClusterInstance a, b; + int soft; +}; + +typedef std::list cluster_instance_pair_list_type; + +struct ClusterIDPair +{ + typedef size_t id_type; + + ClusterIDPair (id_type _a, id_type _b, int _soft) + : a (_a), b (_b), soft (_soft) + { } + + bool operator== (const ClusterIDPair &other) const + { + return a == other.a && b == other.b && soft == other.soft; + } + + bool operator< (const ClusterIDPair &other) const + { + if (!(a == other.a)) { + return a < other.a; + } + if (!(b == other.b)) { + return b < other.b; + } + return soft < other.soft; + } + + id_type a, b; + int soft; +}; inline bool equal_array_delegates (const db::ArrayBase *a, const db::ArrayBase *b) { @@ -1106,7 +1228,7 @@ public: * The "with_id" cluster is removed. All connections of "with_id" are transferred to the * first one. All shapes of "with_id" are transferred to "id". */ - void join_cluster_with(typename local_cluster::id_type id, typename local_cluster::id_type with_id); + void join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id); /** * @brief An iterator delivering all clusters (even the connectors) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 44b817a18..bc865bdc5 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -32,7 +32,9 @@ #include "dbLayoutVsSchematic.h" #include "dbLayoutToNetlistFormatDefs.h" #include "dbLayoutVsSchematicFormatDefs.h" +#include "dbLayoutToNetlistSoftConnections.h" #include "dbShapeProcessor.h" +#include "dbNetlistDeviceClasses.h" #include "dbLog.h" #include "tlGlobPattern.h" @@ -45,7 +47,7 @@ namespace db // Note: the iterator provides the hierarchical selection (enabling/disabling cells etc.) LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) - : m_iter (iter), m_layout_index (0), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false) + : m_iter (iter), m_layout_index (0), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false), m_make_soft_connection_diodes (false) { // check the iterator if (iter.has_complex_region () || iter.region () != db::Box::world ()) { @@ -65,7 +67,7 @@ LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) } LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_index) - : mp_dss (dss), m_layout_index (layout_index), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false) + : mp_dss (dss), m_layout_index (layout_index), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false), m_make_soft_connection_diodes (false) { if (dss->is_valid_layout_index (m_layout_index)) { m_iter = db::RecursiveShapeIterator (dss->layout (m_layout_index), dss->initial_cell (m_layout_index), std::set ()); @@ -73,7 +75,7 @@ LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_i } LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu) - : m_iter (), m_netlist_extracted (false), m_is_flat (true), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false) + : m_iter (), m_netlist_extracted (false), m_is_flat (true), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false), m_make_soft_connection_diodes (false) { mp_internal_dss.reset (new db::DeepShapeStore (topcell_name, dbu)); mp_dss.reset (mp_internal_dss.get ()); @@ -84,7 +86,7 @@ LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu) LayoutToNetlist::LayoutToNetlist () : m_iter (), mp_internal_dss (new db::DeepShapeStore ()), mp_dss (mp_internal_dss.get ()), m_layout_index (0), - m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false) + m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false), m_make_soft_connection_diodes (false) { init (); } @@ -298,6 +300,26 @@ void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::Shap m_conn.connect (dla.layer (), dlb.layer ()); } +void LayoutToNetlist::soft_connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b) +{ + reset_extracted (); + + if (! is_persisted (a)) { + register_layer (a); + } + if (! is_persisted (b)) { + register_layer (b); + } + + // we need to keep a reference, so we can safely delete the region + db::DeepLayer dla = deep_layer_of (a); + db::DeepLayer dlb = deep_layer_of (b); + m_dlrefs.insert (dla); + m_dlrefs.insert (dlb); + + m_conn.soft_connect (dla.layer (), dlb.layer ()); +} + size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const std::string &gn) { reset_extracted (); @@ -313,6 +335,21 @@ size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const return m_conn.connect_global (dl.layer (), gn); } +size_t LayoutToNetlist::soft_connect_global_impl (const db::ShapeCollection &l, const std::string &gn) +{ + reset_extracted (); + + if (! is_persisted (l)) { + register_layer (l); + } + + // we need to keep a reference, so we can safely delete the region + db::DeepLayer dl = deep_layer_of (l); + m_dlrefs.insert (dl); + + return m_conn.soft_connect_global (dl.layer (), gn); +} + const std::string &LayoutToNetlist::global_net_name (size_t id) const { return m_conn.global_net_name (id); @@ -360,6 +397,24 @@ void LayoutToNetlist::join_nets (const tl::GlobPattern &cell, const std::setbegin_circuits (); c != netlist->end_circuits (); ++c) { + const db::Circuit &circuit = *c; + for (auto n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { + const db::Net &net = *n; + if (net.pin_count () > 1) { + ok = false; + tl::error << "Many pins on net " << net.expanded_name () << " in circuit " << circuit.name (); + } + } + } + return ok; +} +#endif + void LayoutToNetlist::extract_netlist () { if (m_netlist_extracted) { @@ -371,6 +426,17 @@ void LayoutToNetlist::extract_netlist () netex.set_include_floating_subcircuits (m_include_floating_subcircuits); netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters); + // treat soft connections + do_soft_connections (); + + // implement the "join_nets" (aka "must connect") feature +#if defined(_DEBUG) + // NOTE: the join_nets feature only works for "one pin per net" case + // TODO: either fix that or make sure we do not get multiple pins per net. + // Right now, there no known case that produces multiple pins on a net at + // this stage. + tl_assert (check_many_pins (mp_netlist.get ())); +#endif do_join_nets (); if (tl::verbosity () >= 41) { @@ -477,7 +543,7 @@ void LayoutToNetlist::check_must_connect (const db::Circuit &c, const db::Net &a check_must_connect_impl (c, a, b, c, a, b, path); } -static std::string path_msg (const std::vector &path, const db::Circuit &c_org) +static std::string path_msg (const std::vector &path) { if (path.empty ()) { return std::string (); @@ -485,15 +551,13 @@ static std::string path_msg (const std::vector &path, co std::string msg (".\n" + tl::to_string (tr ("Instance path: "))); - for (auto p = path.rbegin (); p != path.rend (); ++p) { - if (p != path.rbegin ()) { - msg += "/"; - } - msg += (*p)->circuit ()->name () + ":" + (*p)->expanded_name () + "[" + (*p)->trans ().to_string () + "]"; - } + auto p0 = path.rbegin (); + msg += (*p0)->circuit ()->name (); - msg += "/"; - msg += c_org.name (); + for (auto p = p0; p != path.rend (); ++p) { + msg += "/"; + msg += (*p)->circuit_ref ()->name () + "[" + (*p)->trans ().to_string (true /*short*/) + "]" + ":" + (*p)->expanded_name (); + } return msg; } @@ -519,12 +583,12 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N if (a_org.expanded_name () == b_org.expanded_name ()) { if (path.empty ()) { - db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name ()) + path_msg (path, c_org)); + db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name ()) + path_msg (path)); warn.set_cell_name (c.name ()); warn.set_category_name ("must-connect"); log_entry (warn); } else { - db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s of circuit %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), c_org.name ()) + path_msg (path, c_org)); + db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s of circuit %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), c_org.name ()) + path_msg (path)); warn.set_cell_name (c.name ()); warn.set_geometry (subcircuit_geometry (*path.back (), internal_layout ())); warn.set_category_name ("must-connect"); @@ -532,12 +596,12 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N } } else { if (path.empty ()) { - db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), b_org.expanded_name ()) + path_msg (path, c_org)); + db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), b_org.expanded_name ()) + path_msg (path)); warn.set_cell_name (c.name ()); warn.set_category_name ("must-connect"); log_entry (warn); } else { - db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s of circuit %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), b_org.expanded_name (), c_org.name ()) + path_msg (path, c_org)); + db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s of circuit %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), b_org.expanded_name (), c_org.name ()) + path_msg (path)); warn.set_cell_name (c.name ()); warn.set_geometry (subcircuit_geometry (*path.back (), internal_layout ())); warn.set_category_name ("must-connect"); @@ -558,7 +622,7 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N const db::Net *net_b = sc.net_for_pin (b.begin_pins ()->pin_id ()); if (net_a == 0) { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), a_org.expanded_name (), c_org.name (), subcircuit_to_string (sc)) + path_msg (path, c_org)); + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), a_org.expanded_name (), c_org.name (), subcircuit_to_string (sc)) + path_msg (path)); error.set_cell_name (sc.circuit ()->name ()); error.set_geometry (subcircuit_geometry (sc, internal_layout ())); error.set_category_name ("must-connect"); @@ -566,7 +630,7 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N } if (net_b == 0) { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), b_org.expanded_name (), c_org.name (), subcircuit_to_string (sc)) + path_msg (path, c_org)); + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), b_org.expanded_name (), c_org.name (), subcircuit_to_string (sc)) + path_msg (path)); error.set_cell_name (sc.circuit ()->name ()); error.set_geometry (subcircuit_geometry (sc, internal_layout ())); error.set_category_name ("must-connect"); @@ -584,6 +648,54 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N } } +void LayoutToNetlist::place_soft_connection_diodes () +{ + db::DeviceClassDiode *soft_diode = 0; + + for (auto c = mp_netlist->begin_bottom_up (); c != mp_netlist->end_bottom_up (); ++c) { + + auto clusters = net_clusters ().clusters_per_cell (c->cell_index ()); + + for (auto n = c->begin_nets (); n != c->end_nets (); ++n) { + + auto soft_connections = clusters.upward_soft_connections (n->cluster_id ()); + for (auto sc = soft_connections.begin (); sc != soft_connections.end (); ++sc) { + + if (! soft_diode) { + soft_diode = new db::DeviceClassDiode (); + soft_diode->set_name ("SOFT"); + mp_netlist->add_device_class (soft_diode); + } + + db::Device *sc_device = new db::Device (soft_diode); + c->add_device (sc_device); + + auto nn = c->net_by_cluster_id (*sc); + if (nn) { + sc_device->connect_terminal (db::DeviceClassDiode::terminal_id_C, n.operator-> ()); + sc_device->connect_terminal (db::DeviceClassDiode::terminal_id_A, nn); + } + + } + + } + + } +} + +void LayoutToNetlist::do_soft_connections () +{ + SoftConnectionInfo sc_info; + sc_info.build (*netlist (), net_clusters ()); + sc_info.report (*this); + + if (m_make_soft_connection_diodes) { + place_soft_connection_diodes (); + } else { + sc_info.join_soft_connections (*netlist ()); + } +} + void LayoutToNetlist::do_join_nets () { if (! mp_netlist) { @@ -915,7 +1027,7 @@ LayoutToNetlist::create_layermap (db::Layout &target_layout, int ln) const std::set layers_to_copy; const db::Connectivity &conn = connectivity (); - for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { + for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { layers_to_copy.insert (*layer); } @@ -1162,7 +1274,8 @@ size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell const db::local_clusters &lcc = net_clusters ().clusters_per_cell (cell->cell_index ()); for (db::local_clusters::touching_iterator i = lcc.begin_touching (local_box); ! i.at_end (); ++i) { const db::local_cluster &lc = *i; - if (lc.interacts (test_cluster, trans, m_conn)) { + int soft = 0; + if (lc.interacts (test_cluster, trans, m_conn, soft)) { return lc.id (); } } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 2856de3e9..812ee5d7b 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -494,6 +494,39 @@ public: connect_impl (b, a); } + /** + * @brief Defines an inter-layer soft connection for the given layers. + * The conditions mentioned with intra-layer "connect" apply for this method too. + * The "a" layer is the "upper" and the "b" layer the "lower" layer of the + * soft connection. + */ + void soft_connect (const db::Region &a, const db::Region &b) + { + soft_connect_impl (a, b); + } + + /** + * @brief Defines an inter-layer connection for the given layers. + * As one layer is a texts layer, this connection will basically add net labels. + * The "a" layer is the "upper" and the "b" layer the "lower" layer of the + * soft connection. + */ + void soft_connect (const db::Region &a, const db::Texts &b) + { + soft_connect_impl (a, b); + } + + /** + * @brief Defines an inter-layer connection for the given layers. + * As one layer is a texts layer, this connection will basically add net labels. + * The "a" layer is the "upper" and the "b" layer the "lower" layer of the + * soft connection. + */ + void soft_connect (const db::Texts &a, const db::Region &b) + { + soft_connect_impl (b, a); + } + /** * @brief Connects the given layer with a global net with the given name * Returns the global net ID @@ -512,6 +545,26 @@ public: return connect_global_impl (l, gn); } + /** + * @brief Soft-connects the given layer with a global net with the given name + * Returns the global net ID. + * The global layer is the "lower" layer of the soft connection. + */ + size_t soft_connect_global (const db::Region &l, const std::string &gn) + { + return soft_connect_global_impl (l, gn); + } + + /** + * @brief Soft-connects the given text layer with a global net with the given name + * Returns the global net ID. + * The global layer is the "lower" layer of the soft connection. + */ + size_t soft_connect_global (const db::Texts &l, const std::string &gn) + { + return soft_connect_global_impl (l, gn); + } + /** * @brief Gets the global net name for a given global net ID */ @@ -992,6 +1045,18 @@ public: */ void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self = false, void *parent = 0) const; + // for debugging and testing + bool make_soft_connection_diodes () const + { + return m_make_soft_connection_diodes; + } + + // for debugging and testing + void set_make_soft_connection_diodes (bool f) + { + m_make_soft_connection_diodes = f; + } + private: // no copying LayoutToNetlist (const db::LayoutToNetlist &other); @@ -1021,6 +1086,7 @@ private: std::string m_generator; bool m_include_floating_subcircuits; bool m_top_level_mode; + bool m_make_soft_connection_diodes; std::list m_joined_net_names; std::list > m_joined_net_names_per_cell; std::list > m_joined_nets; @@ -1035,14 +1101,20 @@ private: db::CellMapping make_cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector *nets, bool with_device_cells); void connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b); size_t connect_global_impl (const db::ShapeCollection &l, const std::string &gn); + void soft_connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b); + size_t soft_connect_global_impl (const db::ShapeCollection &l, const std::string &gn); bool is_persisted_impl (const db::ShapeCollection &coll) const; void do_join_nets (db::Circuit &c, const std::vector &nets); void do_join_nets (); + void do_soft_connections (); void join_nets_from_pattern (db::Circuit &c, const tl::GlobPattern &p); void join_nets_from_pattern (db::Circuit &c, const std::set &p); void check_must_connect (const db::Circuit &c, const db::Net &a, const db::Net &b); void check_must_connect_impl (const db::Circuit &c, const db::Net &a, const db::Net &b, const db::Circuit &c_org, const db::Net &a_org, const db::Net &b_org, std::vector &path); + // for debugging and testing + void place_soft_connection_diodes (); + // implementation of NetlistManipulationCallbacks virtual size_t link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &trans); virtual void link_nets (const db::Net *net, const db::Net *with); diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.cc b/src/db/db/dbLayoutToNetlistFormatDefs.cc index 87be9c00f..67eba13c5 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.cc +++ b/src/db/db/dbLayoutToNetlistFormatDefs.cc @@ -39,7 +39,9 @@ namespace l2n_std_format DB_PUBLIC std::string LongKeys::layer_key ("layer"); DB_PUBLIC std::string LongKeys::class_key ("class"); DB_PUBLIC std::string LongKeys::connect_key ("connect"); + DB_PUBLIC std::string LongKeys::softconnect_key ("softconnect"); DB_PUBLIC std::string LongKeys::global_key ("global"); + DB_PUBLIC std::string LongKeys::softglobal_key ("softglobal"); DB_PUBLIC std::string LongKeys::circuit_key ("circuit"); DB_PUBLIC std::string LongKeys::net_key ("net"); DB_PUBLIC std::string LongKeys::name_key ("name"); @@ -72,7 +74,9 @@ namespace l2n_std_format DB_PUBLIC std::string ShortKeys::layer_key ("L"); DB_PUBLIC std::string ShortKeys::class_key ("K"); DB_PUBLIC std::string ShortKeys::connect_key ("C"); + DB_PUBLIC std::string ShortKeys::softconnect_key ("CS"); DB_PUBLIC std::string ShortKeys::global_key ("G"); + DB_PUBLIC std::string ShortKeys::softglobal_key ("GS"); DB_PUBLIC std::string ShortKeys::circuit_key ("X"); DB_PUBLIC std::string ShortKeys::net_key ("N"); DB_PUBLIC std::string ShortKeys::name_key ("I"); diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index 35c41f630..6496ff2f2 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -66,11 +66,14 @@ namespace db * * [connect]: * connect( ...) - connects layer1 with the following layers [short key: C] + * softconnect( ...) + * - specifies soft connection between lower and upper layer [short key: CS] * * [global]: * global( ...) - * - connects the shapes of the layer with the given global - * nets [short key: G] + * - connects the shapes of the layer with the given global nets [short key: G] + * softglobal( ...) + * - soft-connects the shapes of the layer with the given global net [shoft key: GS] * * [circuit]: * circuit( [circuit-def]) - circuit (cell) [short key: X] @@ -222,7 +225,9 @@ namespace l2n_std_format static std::string layer_key; static std::string class_key; static std::string connect_key; + static std::string softconnect_key; static std::string global_key; + static std::string softglobal_key; static std::string circuit_key; static std::string net_key; static std::string name_key; @@ -261,7 +266,9 @@ namespace l2n_std_format static std::string layer_key; static std::string class_key; static std::string connect_key; + static std::string softconnect_key; static std::string global_key; + static std::string softglobal_key; static std::string circuit_key; static std::string net_key; static std::string name_key; diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index 369cad536..1f310c806 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -462,12 +462,17 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo } br.done (); - } else if (l2n && (test (skeys::message_key) || test (lkeys::message_key))) { + } else if (l2n && (test (skeys::softconnect_key) || test (lkeys::softconnect_key))) { - db::LogEntryData data; - read_message_entry (data); - - l2n->log_entry (data); + Brace br (this); + std::string l1; + read_word_or_quoted (l1); + while (br) { + std::string l2; + read_word_or_quoted (l2); + l2n->soft_connect (layer_by_name (l2n, l1), layer_by_name (l2n, l2)); + } + br.done (); } else if (l2n && (test (skeys::global_key) || test (lkeys::global_key))) { @@ -481,6 +486,25 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo } br.done (); + } else if (l2n && (test (skeys::softglobal_key) || test (lkeys::softglobal_key))) { + + Brace br (this); + std::string l1; + read_word_or_quoted (l1); + while (br) { + std::string g; + read_word_or_quoted (g); + l2n->soft_connect_global (layer_by_name (l2n, l1), g); + } + br.done (); + + } else if (l2n && (test (skeys::message_key) || test (lkeys::message_key))) { + + db::LogEntryData data; + read_message_entry (data); + + l2n->log_entry (data); + } else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) { Brace br (this); diff --git a/src/db/db/dbLayoutToNetlistSoftConnections.cc b/src/db/db/dbLayoutToNetlistSoftConnections.cc new file mode 100644 index 000000000..56db8ca9a --- /dev/null +++ b/src/db/db/dbLayoutToNetlistSoftConnections.cc @@ -0,0 +1,422 @@ + + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dbCommon.h" +#include "dbLayoutToNetlistSoftConnections.h" +#include "dbLayoutToNetlist.h" +#include "dbHierNetworkProcessor.h" +#include "dbNetlist.h" + +namespace db +{ + +// ------------------------------------------------------------------------------- +// SoftConnectionNetGraph implementation + +SoftConnectionNetGraph::SoftConnectionNetGraph () + : m_partial_net_count (0) +{ + // .. nothing yet .. +} + +void SoftConnectionNetGraph::add (const db::Net *net, SoftConnectionPinDir dir, const db::Pin *pin, size_t partial_net_count) +{ + m_partial_net_count += partial_net_count; + + // this is where we make the decision about the partial nets ... + if (! pin && dir == SoftConnectionPinDir::down ()) { + m_partial_net_count += 1; + } + + if (pin) { + m_pin_ids.insert (pin->id ()); + } + + m_cluster_dir.insert (std::make_pair (net->cluster_id (), dir)); +} + +// ------------------------------------------------------------------------------- +// SoftConnectionCircuitInfo implementation + +SoftConnectionCircuitInfo::SoftConnectionCircuitInfo (const db::Circuit *circuit) + : mp_circuit (circuit) +{ + // .. nothing yet .. +} + +SoftConnectionNetGraph &SoftConnectionCircuitInfo::make_net_graph () +{ + m_net_graphs.push_back (SoftConnectionNetGraph ()); + return m_net_graphs.back (); +} + +void SoftConnectionCircuitInfo::add_pin_info (const db::Pin *pin, SoftConnectionPinDir dir, SoftConnectionNetGraph *graph_info) +{ + if (pin) { + m_pin_info.insert (std::make_pair (pin->id (), std::make_pair (dir, graph_info))); + } +} + +SoftConnectionPinDir SoftConnectionCircuitInfo::direction_per_pin (const db::Pin *pin) const +{ + if (! pin) { + return SoftConnectionPinDir (); + } + + auto p = m_pin_info.find (pin->id ()); + return p != m_pin_info.end () ? p->second.first : SoftConnectionPinDir (); +} + +const SoftConnectionNetGraph *SoftConnectionCircuitInfo::get_net_graph_per_pin (const db::Pin *pin) const +{ + if (! pin) { + return 0; + } + + auto p = m_pin_info.find (pin->id ()); + return p != m_pin_info.end () ? p->second.second : 0; +} + +// ------------------------------------------------------------------------------- +// SoftConnectionInfo implementation + +SoftConnectionInfo::SoftConnectionInfo () +{ + // .. nothing yet .. +} + +void SoftConnectionInfo::build (const db::Netlist &netlist, const db::hier_clusters &shape_clusters) +{ + for (auto c = netlist.begin_bottom_up (); c != netlist.end_bottom_up (); ++c) { + build_graphs_for_circuit (c.operator-> (), shape_clusters.clusters_per_cell (c->cell_index ())); + } +} + +void SoftConnectionInfo::join_soft_connections (db::Netlist &netlist) +{ + if (tl::verbosity () >= 20) { + tl::info << "Joining soft-connected net graphs .."; + } + + size_t nnet_graphs_tot = 0; + size_t npartial_tot = 0; + + for (auto c = netlist.begin_top_down (); c != netlist.end_top_down (); ++c) { + + size_t nnet_graphs = 0; + size_t npartial = 0; + + auto scc = m_scc_per_circuit.find (c.operator-> ()); + if (scc == m_scc_per_circuit.end ()) { + continue; + } + + const SoftConnectionCircuitInfo &sc_info = scc->second; + + for (auto sc = sc_info.begin (); sc != sc_info.end (); ++sc) { + + auto cc = sc->begin_clusters (); + if (cc != sc->end_clusters ()) { + db::Net *net0 = c->net_by_cluster_id (cc->first); + tl_assert (net0 != 0); + ++nnet_graphs; + while (++cc != sc->end_clusters ()) { + // TODO: logging? + c->join_nets (net0, c->net_by_cluster_id (cc->first)); + ++npartial; + } + } + + } + + nnet_graphs_tot += nnet_graphs; + npartial_tot += npartial; + + if (nnet_graphs > 0 && tl::verbosity () >= 30) { + tl::info << "Circuit " << c->name () << ": joined " << nnet_graphs << " soft-connected net clusters with " << npartial << " partial nets."; + } + + } + + if (tl::verbosity () >= 20) { + tl::info << "Joined " << nnet_graphs_tot << " soft-connected net clusters with " << npartial_tot << " partial nets in total."; + } + + m_scc_per_circuit.clear (); +} + +db::DPolygon +SoftConnectionInfo::representative_polygon (const db::Net *net, const db::LayoutToNetlist &l2n, const db::CplxTrans &trans) +{ + const db::Connectivity &conn = l2n.connectivity (); + const db::hier_clusters &net_clusters = l2n.net_clusters (); + + db::DBox bbox; + + for (auto l = conn.begin_layers (); l != conn.end_layers (); ++l) { + db::recursive_cluster_shape_iterator si (net_clusters, *l, net->circuit ()->cell_index (), net->cluster_id ()); + while (! si.at_end ()) { + if (si->type () == db::NetShape::Polygon) { + bbox += trans * (si.trans () * si->polygon_ref ().box ()); + } + ++si; + } + } + + return db::DPolygon (bbox); +} + +void SoftConnectionInfo::report_partial_nets (const db::Circuit *circuit, const SoftConnectionNetGraph &net_graph, db::LayoutToNetlist &l2n, const std::string &path, const db::DCplxTrans &trans, const std::string &top_cell, int &index, std::set > &seen) +{ + for (auto cc = net_graph.begin_clusters (); cc != net_graph.end_clusters (); ++cc) { + + const db::Net *net = circuit->net_by_cluster_id (cc->first); + + if (! seen.insert (std::make_pair (net, trans)).second) { + continue; + } + + if (cc->second == SoftConnectionPinDir::down () && ! net->is_floating () && net->begin_pins () == net->end_pins ()) { + + std::string msg = tl::sprintf (tl::to_string (tr ("\tPartial net #%d: %s - %s")), ++index, path, net->expanded_name ()); + + db::LogEntryData entry (db::NoSeverity, top_cell, msg); + entry.set_geometry (representative_polygon (net, l2n, trans * db::CplxTrans (l2n.internal_layout ()->dbu ()))); + + l2n.log_entry (entry); + + } + + for (auto sc = net->begin_subcircuit_pins (); sc != net->end_subcircuit_pins (); ++sc) { + + const db::SubCircuit *subcircuit = sc->subcircuit (); + const db::Circuit *circuit_ref = subcircuit->circuit_ref (); + + auto scc = m_scc_per_circuit.find (circuit_ref); + if (scc == m_scc_per_circuit.end ()) { + continue; + } + + const SoftConnectionCircuitInfo &sci = scc->second; + + const SoftConnectionNetGraph *scci = sci.get_net_graph_per_pin (sc->pin ()); + if (! scci || scci->partial_net_count () == 0) { + continue; + } + + std::string p = path; + p += std::string ("/") + circuit_ref->name (); + p += std::string ("[") + subcircuit->trans ().to_string (true /*short*/) + "]:" + subcircuit->expanded_name (); + report_partial_nets (circuit_ref, *scci, l2n, p, trans * subcircuit->trans (), top_cell, index, seen); + + } + + } +} + +void SoftConnectionInfo::report (db::LayoutToNetlist &l2n) +{ + const db::Netlist *netlist = l2n.netlist (); + if (! netlist) { + return; + } + + for (auto c = netlist->begin_bottom_up (); c != netlist->end_bottom_up (); ++c) { + + auto scc = m_scc_per_circuit.find (c.operator-> ()); + if (scc == m_scc_per_circuit.end ()) { + continue; + } + + const SoftConnectionCircuitInfo &sc_info = scc->second; + + for (auto sc = sc_info.begin (); sc != sc_info.end (); ++sc) { + + if (sc->partial_net_count () < 2) { + continue; + } + + db::LogEntryData log_entry (l2n.top_level_mode () ? db::Error : db::Warning, c->name (), tl::to_string (tr ("Net with incomplete wiring (soft-connected partial nets)"))); + log_entry.set_category_name ("soft-connection-check"); + l2n.log_entry (log_entry); + + int index = 0; + std::set > seen; + report_partial_nets (c.operator-> (), *sc, l2n, c->name (), db::DCplxTrans (), c->name (), index, seen); + + } + + } +} + +void SoftConnectionInfo::build_graphs_for_circuit (const db::Circuit *circuit, const db::connected_clusters &shape_clusters) +{ + SoftConnectionCircuitInfo &sc_circuit_info = m_scc_per_circuit.insert (std::make_pair (circuit, SoftConnectionCircuitInfo (circuit))).first->second; + + std::set seen; + for (auto c = shape_clusters.begin (); c != shape_clusters.end (); ++c) { + + if (seen.find (c->id ()) != seen.end ()) { + continue; + } + + // incrementally collect further connected nets (shape clusters) + + std::set connected; + connected.insert (c->id ()); + seen.insert (c->id ()); + + SoftConnectionNetGraph *sc_net_graph = 0; + + while (! connected.empty ()) { + + std::set next_connected; + + for (auto cc = connected.begin (); cc != connected.end (); ++cc) { + + const db::Net *net = circuit->net_by_cluster_id (*cc); + if (! net) { + continue; + } + + // the direction of a net is 0 for "no connections" or "both up and down" + // and -1 for "down-only" connections and +1 for "up-only" connections: + + SoftConnectionPinDir dir; + + // direct soft connections to other nets + + for (int up = 0; up < 2; ++up) { + std::set next = up ? shape_clusters.upward_soft_connections (*cc) : shape_clusters.downward_soft_connections (*cc); + if (! next.empty () || net_has_up_or_down_subcircuit_connections (net, up)) { + dir |= up ? SoftConnectionPinDir::up () : SoftConnectionPinDir::down (); + } + for (auto i = next.begin (); i != next.end (); ++i) { + if (seen.insert (*i).second) { + next_connected.insert (*i); + } + } + } + + // collect soft connections via subcircuits + + size_t sc_partial_net_count = 0; + std::set next = net_connections_through_subcircuits (net, sc_partial_net_count); + + for (auto i = next.begin (); i != next.end (); ++i) { + if (seen.insert (*i).second) { + next_connected.insert (*i); + } + } + + // is this net associated with a pin? + + const db::Pin *pin = 0; + if (net->begin_pins () != net->end_pins ()) { + // TODO: multiple pins per net need to be supported? + tl_assert (net->pin_count () == 1); + pin = net->begin_pins ()->pin (); + } + + if (! sc_net_graph) { + sc_net_graph = &sc_circuit_info.make_net_graph (); + } + + // we do not count floating nets as they cannot make a functional connection + if (! net->is_floating ()) { + sc_net_graph->add (net, dir, pin, sc_partial_net_count); + } + + sc_circuit_info.add_pin_info (pin, dir, sc_net_graph); + + } + + connected.swap (next_connected); + + } + + } + +} + +bool SoftConnectionInfo::net_has_up_or_down_subcircuit_connections (const db::Net *net, bool up) +{ + SoftConnectionPinDir look_for_dir = up ? SoftConnectionPinDir::up () : SoftConnectionPinDir::down (); + + for (auto sc = net->begin_subcircuit_pins (); sc != net->end_subcircuit_pins (); ++sc) { + const db::Pin *pin = sc->pin (); + const db::Circuit *ref = sc->subcircuit ()->circuit_ref (); + auto scc_ref = m_scc_per_circuit.find (ref); + if (scc_ref != m_scc_per_circuit.end ()) { + SoftConnectionPinDir dir = scc_ref->second.direction_per_pin (pin); + if (dir & look_for_dir) { + return true; + } + } + } + + return false; +} + +void SoftConnectionInfo::get_net_connections_through_subcircuit (const db::SubCircuit *subcircuit, const db::Pin *pin, std::set &ids, size_t &partial_net_count) +{ + auto scc = m_scc_per_circuit.find (subcircuit->circuit_ref ()); + if (scc != m_scc_per_circuit.end ()) { + + const SoftConnectionCircuitInfo &sci = scc->second; + + const SoftConnectionNetGraph *scci = sci.get_net_graph_per_pin (pin); + if (scci) { + + // NOTE: limiting the partial net count here means we do report a partially connected once in the + // hierarchy, not on every level. + // Say, if you have two subcircuits, one (A) having 2 partial nets and the other (B) none. Then + // (A) would be reported to partial nets only and when combining (A) and (B) we just need to check + // whether B would also have partial nets. By not taking 2 + 0, but 1 + 0 the combination of (A) + // and (B) does not give an error (error = number of partial nets > 1). + partial_net_count += std::min (size_t (1), scci->partial_net_count ()); + + for (auto p = scci->begin_pins (); p != scci->end_pins (); ++p) { + if (*p != pin->id ()) { + const NetSubcircuitPinRef *netref = subcircuit->netref_for_pin (*p); + if (netref && netref->net ()) { + ids.insert (netref->net ()->cluster_id ()); + } + } + } + + } + + } +} + +std::set SoftConnectionInfo::net_connections_through_subcircuits (const db::Net *net, size_t &partial_net_count) +{ + std::set ids; + for (auto sc = net->begin_subcircuit_pins (); sc != net->end_subcircuit_pins (); ++sc) { + get_net_connections_through_subcircuit (sc->subcircuit (), sc->pin (), ids, partial_net_count); + } + return ids; +} + +} diff --git a/src/db/db/dbLayoutToNetlistSoftConnections.h b/src/db/db/dbLayoutToNetlistSoftConnections.h new file mode 100644 index 000000000..dbc65698f --- /dev/null +++ b/src/db/db/dbLayoutToNetlistSoftConnections.h @@ -0,0 +1,353 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_dbLayoutToNetlistSoftConnections +#define _HDR_dbLayoutToNetlistSoftConnections + +#include "dbCommon.h" +#include "dbPolygon.h" + +#include +#include +#include +#include +#include + +namespace db +{ + +class Net; +class Pin; +class Circuit; +class Netlist; +class SubCircuit; +class LayoutToNetlist; + +template class hier_clusters; +template class connected_clusters; +class NetShape; + +/** + * @brief A small struct representing a direction value for a pin + * + * The pin can be upward, downward connected, connected in both ways or not connected at all. + */ +class SoftConnectionPinDir +{ +public: + /** + * @brief Constructs from a single direction (+1: up, -1: down) + */ + explicit SoftConnectionPinDir (int dir = 0) + : m_flags (0) + { + if (dir > 0) { + m_flags = 1; + } else if (dir < 0) { + m_flags = 2; + } + } + + /** + * @brief Equality + */ + bool operator== (SoftConnectionPinDir other) const + { + return m_flags == other.m_flags; + } + + /** + * @brief Inequality + */ + bool operator!= (SoftConnectionPinDir other) const + { + return m_flags != other.m_flags; + } + + /** + * @brief Join two value + */ + SoftConnectionPinDir operator| (SoftConnectionPinDir other) const + { + SoftConnectionPinDir res = *this; + res.m_flags |= other.m_flags; + return res; + } + + SoftConnectionPinDir &operator|= (SoftConnectionPinDir other) + { + m_flags |= other.m_flags; + return *this; + } + + /** + * @brief Test for one direction + */ + bool operator& (SoftConnectionPinDir other) const + { + return (m_flags & other.m_flags) != 0; + } + + /** + * @brief Static getters for the constants + */ + + inline static SoftConnectionPinDir none () { return SoftConnectionPinDir (0); } + inline static SoftConnectionPinDir up () { return SoftConnectionPinDir (1); } + inline static SoftConnectionPinDir down () { return SoftConnectionPinDir (-1); } + inline static SoftConnectionPinDir both () { return up () | down (); } + +private: + unsigned int m_flags; +}; + +/** + * @brief Describes a soft-connected net graph + * + * Such a graph is a collection of nets/shape clusters that are connected via + * soft connections. + * There is also some information about the count of "down-only" nets. With + * this, this object can serve as a representative model for a circuit's + * content as embedded into a larger graph through subcircuits. + * + * A circuit in general can be made from a number of such net graphs. + */ +class DB_PUBLIC SoftConnectionNetGraph +{ +public: + typedef std::set pin_set; + typedef pin_set::const_iterator pin_iterator; + typedef std::map dir_map; + typedef dir_map::const_iterator dir_map_iterator; + + SoftConnectionNetGraph (); + + /** + * @brief Enters information about a specific net + * + * @param net The Net for which we are entering information + * @param dir The direction code of the net + * @param pin A pin that might leading outside our current circuit from this net (0 if there is none) + * @param partial_net_count The partial net count of nets attached to this net inside subcircuits + */ + void add (const db::Net *net, SoftConnectionPinDir dir, const db::Pin *pin, size_t partial_net_count); + + /** + * @brief Gets the partial net count + * + * The partial net count is the number of nets definitely isolated. + * This is the count of "down-only" connected nets on the cluster. + * This may also involve nets from subcircuits. + * Only non-trivial (floating) nets are counted. + * + * A partial net count of more than one indicates a soft connection + * between nets. + */ + size_t partial_net_count () const + { + return m_partial_net_count; + } + + /** + * @brief Gets the outside pins on the net graph (begin iterator) + * + * The iterator delivers Pin IDs of pins leading outside the circuit this graph lives in. + */ + pin_iterator begin_pins () const + { + return m_pin_ids.begin (); + } + + /** + * @brief Gets the pins on the net graph (end iterator) + */ + pin_iterator end_pins () const + { + return m_pin_ids.end (); + } + + /** + * @brief Gets the shape clusters + dir information (begin iterator) + */ + dir_map_iterator begin_clusters () const + { + return m_cluster_dir.begin (); + } + + /** + * @brief Gets the shape clusters + dir information (end iterator) + */ + dir_map_iterator end_clusters () const + { + return m_cluster_dir.end (); + } + +private: + pin_set m_pin_ids; + size_t m_partial_net_count; + dir_map m_cluster_dir; +}; + +/** + * @brief Provides temporary soft connection information for a circuit + * + * Soft connection information is the soft-connected net graphs that are formed inside + * the circuit and how these graphs connect to pins from the circuit leading outside. + */ +class DB_PUBLIC SoftConnectionCircuitInfo +{ +public: + typedef std::list net_graph_list; + typedef net_graph_list::const_iterator net_graph_list_iterator; + + /** + * @brief Constructor + */ + SoftConnectionCircuitInfo (const db::Circuit *circuit); + + /** + * @brief Gets the circuit for this info object + */ + const db::Circuit *circuit () const + { + return mp_circuit; + } + + /** + * @brief Creates a new graph info object + */ + SoftConnectionNetGraph &make_net_graph (); + + /** + * @brief Adds information about a pin + * + * @param pin The pin + * @param dir The direction of connections from the pin + * @param graph_info The soft-connected net graph info object + */ + void add_pin_info (const db::Pin *pin, SoftConnectionPinDir dir, SoftConnectionNetGraph *graph_info); + + /** + * @brief Gets the direction attribute of the pin + */ + SoftConnectionPinDir direction_per_pin (const db::Pin *pin) const; + + /** + * @brief Gets the soft-connected net graph object the pin connects to + */ + const SoftConnectionNetGraph *get_net_graph_per_pin (const db::Pin *pin) const; + + /** + * @brief List of per-circuit net graph objects, begin iterator + */ + net_graph_list_iterator begin () const + { + return m_net_graphs.begin (); + } + + /** + * @brief List of per-circuit net graph objects, end iterator + */ + net_graph_list_iterator end () const + { + return m_net_graphs.end (); + } + +private: + const db::Circuit *mp_circuit; + net_graph_list m_net_graphs; + std::map > m_pin_info; +}; + +/** + * @brief Provides temporary soft connection information for a netlist + */ +class DB_PUBLIC SoftConnectionInfo +{ +public: + SoftConnectionInfo (); + + /** + * @brief Builds the soft connection information for the given netlist and shape clusters + */ + void build (const db::Netlist &netlist, const db::hier_clusters &shape_clusters); + + /** + * @brief Joins nets connected by soft connections + * + * This method will clear the information from this object + * as the clusters will no longer be valid. + */ + void join_soft_connections (db::Netlist &netlist); + + /** + * @brief Create log entries + */ + void report (db::LayoutToNetlist &l2n); + +private: + std::map m_scc_per_circuit; + + /** + * @brief Builds the per-circuit net graphs + * + * First of all, this method creates a SoftConnectionCircuitInfo object for the circuit. + * + * Inside this per-circuit object, it will create a number of SoftConnectionNetGraph objects - each one + * for a cluster of soft-connected nets. + * + * Call this method bottom-up as it needs SoftConnectionCircuitInfo objects for called circuits. + */ + void build_graphs_for_circuit (const db::Circuit *circuit, const db::connected_clusters &shape_clusters); + + /** + * @brief Gets a value indicating whether the given net connects to subcircuits with up or down connections inside + */ + bool net_has_up_or_down_subcircuit_connections (const db::Net *net, bool up); + + /** + * @brief Gets connections to other nets / shape clusters through the given subcircuit from the given pin + * + * As a side effect, this method will also collect the partial net count - that is the number + * of defintively disconnected (down-only) nets. + * More that one such a net will render an error. + */ + void get_net_connections_through_subcircuit (const db::SubCircuit *subcircuit, const db::Pin *pin, std::set &ids, size_t &partial_net_count); + + /** + * @brief Gets connections to other nets / shape clusters through the subcircuits on the net + * + * As a side effect, this method will also collect the partial net count - that is the number + * of defintively disconnected (down-only) nets. + * More that one such a net will render an error. + * + * The return value is a set of nets shape cluster IDs. + */ + std::set net_connections_through_subcircuits (const db::Net *net, size_t &partial_net_count); + + void report_partial_nets (const db::Circuit *circuit, const SoftConnectionNetGraph &cluster_info, LayoutToNetlist &l2n, const std::string &path, const db::DCplxTrans &trans, const std::string &top_cell, int &index, std::set > &seen); + db::DPolygon representative_polygon (const db::Net *net, const db::LayoutToNetlist &l2n, const db::CplxTrans &trans); +}; + +} + +#endif diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index 86cbba3cb..39caaa1ce 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -351,7 +351,7 @@ void std_writer_impl::write (bool nested, TokenizedOutput &stream, std::ma if (! Keys::is_short ()) { stream << endl << "# Mask layers" << endl; } - for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) { + for (db::Connectivity::all_layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) { TokenizedOutput out (stream, Keys::layer_key); out << name_for_layer (mp_l2n, *l); db::LayerProperties lp = ly->get_properties (*l); @@ -364,15 +364,32 @@ void std_writer_impl::write (bool nested, TokenizedOutput &stream, std::ma if (! Keys::is_short ()) { stream << endl << "# Mask layer connectivity" << endl; } - for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) { + for (db::Connectivity::all_layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) { db::Connectivity::layer_iterator ce = mp_l2n->connectivity ().end_connected (*l); db::Connectivity::layer_iterator cb = mp_l2n->connectivity ().begin_connected (*l); if (cb != ce) { - TokenizedOutput out (stream, Keys::connect_key); - out << name_for_layer (mp_l2n, *l); - for (db::Connectivity::layer_iterator c = mp_l2n->connectivity ().begin_connected (*l); c != ce; ++c) { - out << name_for_layer (mp_l2n, *c); + bool any_soft = false; + { + TokenizedOutput out (stream, Keys::connect_key); + out << name_for_layer (mp_l2n, *l); + for (db::Connectivity::layer_iterator c = mp_l2n->connectivity ().begin_connected (*l); c != ce; ++c) { + if (c->second < 0) { + any_soft = true; + } + out << name_for_layer (mp_l2n, c->first); + } + } + // add soft connections in addition and as overrides to stay backward compatible with older versions + // (these will ignore these statements) + if (any_soft) { + TokenizedOutput out (stream, Keys::softconnect_key); + out << name_for_layer (mp_l2n, *l); + for (db::Connectivity::layer_iterator c = mp_l2n->connectivity ().begin_connected (*l); c != ce; ++c) { + if (c->second < 0) { + out << name_for_layer (mp_l2n, c->first); + } + } } m_progress.set (mp_stream->pos ()); } @@ -380,7 +397,7 @@ void std_writer_impl::write (bool nested, TokenizedOutput &stream, std::ma } any = false; - for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) { + for (db::Connectivity::all_layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) { db::Connectivity::global_nets_iterator ge = mp_l2n->connectivity ().end_global_connections (*l); db::Connectivity::global_nets_iterator gb = mp_l2n->connectivity ().begin_global_connections (*l); @@ -391,10 +408,27 @@ void std_writer_impl::write (bool nested, TokenizedOutput &stream, std::ma } any = true; } - TokenizedOutput out (stream, Keys::global_key); - out << name_for_layer (mp_l2n, *l); - for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) { - out << tl::to_word_or_quoted_string (mp_l2n->connectivity ().global_net_name (*g)); + bool any_soft = false; + { + TokenizedOutput out (stream, Keys::global_key); + out << name_for_layer (mp_l2n, *l); + for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) { + if (g->second < 0) { + any_soft = true; + } + out << tl::to_word_or_quoted_string (mp_l2n->connectivity ().global_net_name (g->first)); + } + } + // add soft connections in addition and as overrides to stay backward compatible with older versions + // (these will ignore these statements) + if (any_soft) { + TokenizedOutput out (stream, Keys::softglobal_key); + out << name_for_layer (mp_l2n, *l); + for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) { + if (g->second < 0) { + out << tl::to_word_or_quoted_string (mp_l2n->connectivity ().global_net_name (g->first)); + } + } } m_progress.set (mp_stream->pos ()); } @@ -634,7 +668,7 @@ void std_writer_impl::write (TokenizedOutput &stream, const db::Net &net, reset_geometry_ref (); - for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + for (db::Connectivity::all_layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { db::cell_index_type cci = circuit->cell_index (); db::cell_index_type prev_ci = cci; @@ -757,7 +791,7 @@ void std_writer_impl::write (TokenizedOutput &stream, const db::DeviceAbst bool any = false; - for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + for (db::Connectivity::all_layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { size_t cid = device_abstract.cluster_id_for_terminal (t->id ()); if (cid == 0) { diff --git a/src/db/db/dbLog.cc b/src/db/db/dbLog.cc index a99f2a864..67fef958e 100644 --- a/src/db/db/dbLog.cc +++ b/src/db/db/dbLog.cc @@ -161,22 +161,33 @@ std::string LogEntryData::to_string (bool with_geometry) const { std::string res; + const std::string &msg = message (); - if (m_category_name != 0) { - if (m_category_description == 0) { - res += "[" + category_name () + "] "; - } else { - res += "[" + category_description () + "] "; + // a message that starts with a tab is a continuation or detail entry + if (! msg.empty () && msg [0] == '\t') { + + res = " "; + res += std::string (msg, 1); + + } else { + + if (m_category_name != 0) { + if (m_category_description == 0) { + res += "[" + category_name () + "] "; + } else { + res += "[" + category_description () + "] "; + } } - } - if (m_cell_name != 0) { - res += tl::to_string (tr ("In cell ")); - res += cell_name (); - res += ": "; - } + if (m_cell_name != 0) { + res += tl::to_string (tr ("In cell ")); + res += cell_name (); + res += ": "; + } - res += message (); + res += msg; + + } if (with_geometry && ! m_geometry.box ().empty ()) { res += tl::to_string (tr (", shape: ")) + m_geometry.to_string (); diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index ddcef72c2..dd4ac0c23 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -288,7 +288,9 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo const db::local_cluster &lc = clusters.cluster_by_id (*c); const connected_clusters_type::connections_type &cc = clusters.connections_for_cluster (*c); - if (cc.empty () && lc.empty ()) { + const std::set &sc_up = clusters.upward_soft_connections (*c); + const std::set &sc_down = 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. continue; diff --git a/src/db/db/gsiDeclDbHierNetworkProcessor.cc b/src/db/db/gsiDeclDbHierNetworkProcessor.cc index 7e05ccbeb..f4727e2d3 100644 --- a/src/db/db/gsiDeclDbHierNetworkProcessor.cc +++ b/src/db/db/gsiDeclDbHierNetworkProcessor.cc @@ -26,22 +26,106 @@ namespace gsi { +static std::string +l2s (db::Connectivity::layer_iterator b, db::Connectivity::layer_iterator e) +{ + std::string s; + for (db::Connectivity::layer_iterator i = b; i != e; ++i) { + if (! s.empty ()) { + s += ","; + } + s += tl::to_string (i->first); + if (i->second < 0) { + s += "-S"; + } else if (i->second > 0) { + s += "+S"; + } + } + return s; +} + +static std::string +gn2s (db::Connectivity::global_nets_iterator b, db::Connectivity::global_nets_iterator e) +{ + std::string s; + for (db::Connectivity::global_nets_iterator i = b; i != e; ++i) { + if (! s.empty ()) { + s += ","; + } + s += tl::to_string (i->first); + if (i->second < 0) { + s += "-S"; + } else if (i->second > 0) { + s += "+S"; + } + } + return s; +} + +static std::string +connectivity_to_string (const db::Connectivity *conn) +{ + std::string res; + + for (auto l = conn->begin_layers (); l != conn->end_layers (); ++l) { + if (conn->begin_connected (*l) != conn->end_connected (*l)) { + if (! res.empty ()) { + res += "\n"; + } + res += tl::to_string (*l) + ":" + l2s (conn->begin_connected (*l), conn->end_connected (*l)); + } + if (conn->begin_global_connections (*l) != conn->end_global_connections (*l)) { + if (! res.empty ()) { + res += "\n"; + } + res += "G" + tl::to_string (*l) + ":" + gn2s (conn->begin_global_connections (*l), conn->end_global_connections (*l)); + } + } + + return res; +} + Class decl_dbConnectivity ("db", "Connectivity", gsi::method ("connect", (void (db::Connectivity::*) (unsigned int)) &db::Connectivity::connect, gsi::arg ("layer"), "@brief Specifies intra-layer connectivity.\n" + "This method specifies a hard connection between shapes on the given layer. " + "Without specifying such a connection, shapes on that layer do not form connection regions." ) + gsi::method ("connect", (void (db::Connectivity::*) (unsigned int, unsigned int)) &db::Connectivity::connect, gsi::arg ("layer_a"), gsi::arg ("layer_b"), "@brief Specifies inter-layer connectivity.\n" + "This method specifies a hard connection between shapes on layer_a and layer_b." + ) + + gsi::method ("soft_connect", (void (db::Connectivity::*) (unsigned int, unsigned int)) &db::Connectivity::soft_connect, gsi::arg ("layer_a"), gsi::arg ("layer_b"), + "@brief Specifies a soft connection between layer_a and layer_b.\n" + "@param layer_a The 'upper' layer\n" + "@param layer_b The 'lower' layer\n" + "Soft connections are made between a lower and an upper layer. The lower layer conceptually is a high-ohmic " + "(i.e. substrate, diffusion) region that is not intended for signal wiring. The netlist extraction will check " + "that no routing happens over such regions.\n" + "\n" + "Soft connections have in introduced in version 0.29." ) + gsi::method ("connect_global", (size_t (db::Connectivity::*) (unsigned int, const std::string &)) &db::Connectivity::connect_global, gsi::arg ("layer"), gsi::arg ("global_net_name"), "@brief Connects the given layer to the global net given by name.\n" "Returns the ID of the global net." ) + + gsi::method ("soft_connect_global", (size_t (db::Connectivity::*) (unsigned int, const std::string &)) &db::Connectivity::soft_connect_global, gsi::arg ("layer"), gsi::arg ("global_net_name"), + "@brief Soft-connects the given layer to the global net given by name.\n" + "Returns the ID of the global net.\n" + "See \\soft_connect for a description of the soft connection feature. The global net is always the " + "'lower' (i.e. high-ohmic, substrate) part of the soft connection.\n" + "\n" + "Soft connections have in introduced in version 0.29." + ) + gsi::method ("global_net_name", &db::Connectivity::global_net_name, gsi::arg ("global_net_id"), "@brief Gets the name for a given global net ID.\n" ) + gsi::method ("global_net_id", &db::Connectivity::global_net_id, gsi::arg ("global_net_name"), "@brief Gets the ID for a given global net name.\n" + ) + + // provided for testing purposes mainly. + gsi::method_ext ("to_s", &connectivity_to_string, + "@hide\n" ), "@brief This class specifies connections between different layers.\n" "Connections are build using \\connect. There are basically two flavours of connections: intra-layer and inter-layer.\n" @@ -60,6 +144,10 @@ Class decl_dbConnectivity ("db", "Connectivity", "Global nets are defined by name and are managed through IDs. To get the name for a given ID, use " "\\global_net_name." "\n" + "Starting with version 0.29, soft connections are supported. Soft connections attach to high-ohmic substrate or diffusion " + "layers (the 'lower' layer) are upon netlist extraction it will be checked that no wiring is routed over such connections. " + "See \\soft_connect and \\soft_global_connect for details.\n" + "\n" "This class has been introduced in version 0.26.\n" ); diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index a71ae0eaa..a9c06d59d 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -433,6 +433,32 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "This variant has been introduced in version 0.27.\n" ) + + gsi::method ("soft_connect", (void (db::LayoutToNetlist::*) (const db::Region &, const db::Region &)) &db::LayoutToNetlist::soft_connect, gsi::arg ("a"), gsi::arg ("b"), + "@brief Defines an inter-layer connection for the given layers in soft mode.\n" + "Connects two layers through a soft connection.\n" + "Soft connections cannot make connections between two different nets.\n" + "These are directional connections where 'b' is the 'lower' layer (typically high-ohmic substrate or diffusion).\n" + "\n" + "Soft connections have been introduced in version 0.29.\n" + ) + + gsi::method ("soft_connect", (void (db::LayoutToNetlist::*) (const db::Region &, const db::Texts &)) &db::LayoutToNetlist::soft_connect, gsi::arg ("a"), gsi::arg ("b"), + "@brief Defines an inter-layer connection for the given layers in soft mode.\n" + "Connects two layers through a soft connection.\n" + "Soft connections cannot make connections between two different nets.\n" + "These are directional connections where 'b' is the 'lower' layer (typically high-ohmic substrate or diffusion).\n" + "As one argument is a (hierarchical) text collection, this method is used to attach net labels to polygons.\n" + "\n" + "Soft connections have been introduced in version 0.29.\n" + ) + + gsi::method ("soft_connect", (void (db::LayoutToNetlist::*) (const db::Texts &, const db::Region &)) &db::LayoutToNetlist::soft_connect, gsi::arg ("a"), gsi::arg ("b"), + "@brief Defines an inter-layer connection for the given layers in soft mode.\n" + "Connects two layers through a soft connection.\n" + "Soft connections cannot make connections between two different nets.\n" + "These are directional connections where 'b' is the 'lower' layer (typically high-ohmic substrate or diffusion).\n" + "As one argument is a (hierarchical) text collection, this method is used to attach net labels to polygons.\n" + "\n" + "Soft connections have been introduced in version 0.29.\n" + ) + gsi::method ("connect_global", (size_t (db::LayoutToNetlist::*) (const db::Region &, const std::string &)) &db::LayoutToNetlist::connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"), "@brief Defines a connection of the given layer with a global net.\n" "This method returns the ID of the global net. Use \\global_net_name to get " @@ -441,10 +467,26 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("connect_global", (size_t (db::LayoutToNetlist::*) (const db::Texts &, const std::string &)) &db::LayoutToNetlist::connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"), "@brief Defines a connection of the given text layer with a global net.\n" "This method returns the ID of the global net. Use \\global_net_name to get " - "the name back from the ID." + "the name back from the ID.\n" "\n" "This variant has been introduced in version 0.27.\n" ) + + gsi::method ("soft_connect_global", (size_t (db::LayoutToNetlist::*) (const db::Region &, const std::string &)) &db::LayoutToNetlist::soft_connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"), + "@brief Defines a connection of the given layer with a global net in soft mode.\n" + "This method returns the ID of the global net. Use \\global_net_name to get " + "the name back from the ID.\n" + "Soft connections are directional, where the global net is the 'lower' layer (typically high-ohmic substrate or diffusion).\n" + "\n" + "Soft connections have been introduced in version 0.29.\n" + ) + + gsi::method ("soft_connect_global", (size_t (db::LayoutToNetlist::*) (const db::Texts &, const std::string &)) &db::LayoutToNetlist::soft_connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"), + "@brief Defines a connection of the given text layer with a global net in soft mode.\n" + "This method returns the ID of the global net. Use \\global_net_name to get " + "the name back from the ID.\n" + "Soft connections are directional, where the global net is the 'lower' layer (typically high-ohmic substrate or diffusion).\n" + "\n" + "Soft connections have been introduced in version 0.29.\n" + ) + gsi::method ("global_net_name", &db::LayoutToNetlist::global_net_name, gsi::arg ("global_net_id"), "@brief Gets the global net name for the given global net ID." ) + @@ -464,6 +506,12 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "This attribute has been introduced in version 0.27.\n" ) + + gsi::method ("make_soft_connection_diodes=", &db::LayoutToNetlist::set_make_soft_connection_diodes, gsi::arg ("flag"), + "@hide" + ) + + gsi::method ("make_soft_connection_diodes", &db::LayoutToNetlist::make_soft_connection_diodes, + "@hide" + ) + gsi::method ("top_level_mode=", &db::LayoutToNetlist::set_top_level_mode, gsi::arg ("flag"), "@brief Sets a flag indicating whether top level mode is enabled.\n" "\n" diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 7ad05b9a3..60017754d 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -36,6 +36,23 @@ static std::string l2s (db::Connectivity::layer_iterator b, db::Connectivity::la { std::string s; for (db::Connectivity::layer_iterator i = b; i != e; ++i) { + if (! s.empty ()) { + s += ","; + } + s += tl::to_string (i->first); + if (i->second < 0) { + s += "-S"; + } else if (i->second > 0) { + s += "+S"; + } + } + return s; +} + +static std::string al2s (db::Connectivity::all_layer_iterator b, db::Connectivity::all_layer_iterator e) +{ + std::string s; + for (db::Connectivity::all_layer_iterator i = b; i != e; ++i) { if (! s.empty ()) { s += ","; } @@ -51,7 +68,12 @@ static std::string gn2s (db::Connectivity::global_nets_iterator b, db::Connectiv if (! s.empty ()) { s += ","; } - s += tl::to_string (*i); + s += tl::to_string (i->first); + if (i->second < 0) { + s += "-S"; + } else if (i->second > 0) { + s += "+S"; + } } return s; } @@ -60,15 +82,15 @@ TEST(1_Connectivity) { db::Connectivity conn; - EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), ""); + EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), ""); conn.connect (0); - EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), "0"); + EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), "0"); EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0"); EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), ""); conn.connect (0, 1); - EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), "0,1"); + EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), "0,1"); EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1"); EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0"); @@ -104,6 +126,60 @@ TEST(1_Connectivity) EXPECT_EQ (conn2.global_net_name (1), "GLOBAL2"); } +TEST(1_ConnectivitySoft) +{ + db::Connectivity conn; + + EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), ""); + + conn.connect (0); + EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), "0"); + EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0"); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), ""); + + conn.soft_connect (0, 1); + EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), "0,1"); + EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1-S"); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0+S"); + + conn.connect (1); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0+S,1"); + + conn.soft_connect (2, 0); + conn.connect (2); + + EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1-S,2+S"); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0+S,1"); + EXPECT_EQ (l2s (conn.begin_connected (2), conn.end_connected (2)), "0-S,2"); + + conn.connect (2, 0); + + EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1-S,2"); + EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0+S,1"); + EXPECT_EQ (l2s (conn.begin_connected (2), conn.end_connected (2)), "0,2"); + + EXPECT_EQ (conn.soft_connect_global (0, "GLOBAL"), size_t (0)); + EXPECT_EQ (gn2s (conn.begin_global_connections (2), conn.end_global_connections (2)), ""); + EXPECT_EQ (gn2s (conn.begin_global_connections (0), conn.end_global_connections (0)), "0-S"); + EXPECT_EQ (conn.soft_connect_global (2, "GLOBAL2"), size_t (1)); + EXPECT_EQ (gn2s (conn.begin_global_connections (2), conn.end_global_connections (2)), "1-S"); + EXPECT_EQ (conn.connect_global (0, "GLOBAL2"), size_t (1)); + EXPECT_EQ (gn2s (conn.begin_global_connections (0), conn.end_global_connections (0)), "0-S,1"); + + EXPECT_EQ (conn.global_net_name (0), "GLOBAL"); + EXPECT_EQ (conn.global_net_name (1), "GLOBAL2"); + + db::Connectivity conn2 = conn; + + EXPECT_EQ (l2s (conn2.begin_connected (0), conn2.end_connected (0)), "0,1-S,2"); + EXPECT_EQ (l2s (conn2.begin_connected (1), conn2.end_connected (1)), "0+S,1"); + EXPECT_EQ (l2s (conn2.begin_connected (2), conn2.end_connected (2)), "0,2"); + + EXPECT_EQ (gn2s (conn2.begin_global_connections (0), conn2.end_global_connections (0)), "0-S,1"); + EXPECT_EQ (conn2.global_net_name (0), "GLOBAL"); + EXPECT_EQ (conn2.global_net_name (1), "GLOBAL2"); +} + TEST(2_ShapeInteractions) { db::Connectivity conn; @@ -121,18 +197,19 @@ TEST(2_ShapeInteractions) db::ICplxTrans t3 (db::Trans (db::Vector (0, 2000))); db::PolygonRef ref3 (poly.transformed (t3), repo); - EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true); - EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2), true); // t2*ref1 == ref2 - EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true); - EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2), true); - EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true); - EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2), true); - EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false); - EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3), false); // t3*ref1 == ref3 - EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false); - EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3), false); - EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false); - EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2), false); + int soft = std::numeric_limits::max (); + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0, soft), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2, soft), true); // t2*ref1 == ref2 + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1, soft), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2, soft), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0, soft), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2, soft), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0, soft), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3, soft), false); // t3*ref1 == ref3 + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1, soft), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3, soft), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2, soft), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2, soft), false); } TEST(2_ShapeInteractionsRealPolygon) @@ -154,20 +231,21 @@ TEST(2_ShapeInteractionsRealPolygon) db::ICplxTrans t4 (db::Trans (db::Vector (0, 1500))); db::PolygonRef ref4 (poly.transformed (t4), repo); - EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true); - EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2), true); // t2*ref1 == ref2 - EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true); - EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2), true); - EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true); - EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2), true); - EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false); - EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3), false); - EXPECT_EQ (conn.interacts (ref1, 0, ref4, 0), true); - EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t4), true); - EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false); - EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3), false); - EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false); - EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2), false); + int soft = std::numeric_limits::max (); + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0, soft), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2, soft), true); // t2*ref1 == ref2 + EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1, soft), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2, soft), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0, soft), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2, soft), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0, soft), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3, soft), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref4, 0, soft), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t4, soft), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1, soft), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3, soft), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2, soft), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2, soft), false); } TEST(10_LocalClusterBasic) @@ -215,21 +293,22 @@ TEST(11_LocalClusterInteractBasic) db::local_cluster cluster; db::local_cluster cluster2; + int soft; - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false); cluster.add (db::PolygonRef (poly, repo), 0); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false); cluster2.add (db::PolygonRef (poly, repo), 0); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn), true); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn), true); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn), false); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn, soft), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn, soft), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn, soft), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn, soft), false); cluster.clear (); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false); } TEST(11_LocalClusterInteractDifferentLayers) @@ -248,28 +327,29 @@ TEST(11_LocalClusterInteractDifferentLayers) db::local_cluster cluster; db::local_cluster cluster2; + int soft; - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false); cluster.add (db::PolygonRef (poly, repo), 0); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false); cluster2.add (db::PolygonRef (poly, repo), 1); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn), true); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn), true); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn), false); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn, soft), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn, soft), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn, soft), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn, soft), false); cluster.clear (); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false); cluster.add (db::PolygonRef (poly, repo), 2); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); // not connected + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false); // not connected cluster.clear (); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false); cluster.add (db::PolygonRef (poly, repo), 1); - EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true); + EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), true); } static std::string obj2string (const db::PolygonRef &ref) @@ -286,7 +366,7 @@ template static std::string local_cluster_to_string (const db::local_cluster &cluster, const db::Connectivity &conn) { std::string res; - for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + for (db::Connectivity::all_layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { for (typename db::local_cluster::shape_iterator s = cluster.begin (*l); ! s.at_end (); ++s) { if (! res.empty ()) { res += ";"; @@ -313,6 +393,15 @@ static std::string local_clusters_to_string (const db::local_clusters &cluste } s += "#" + tl::to_string (c->id ()) + ":" + local_cluster_to_string (*c, conn); } + for (typename db::local_clusters::const_iterator c = clusters.begin (); c != clusters.end (); ++c) { + auto sc = clusters.upward_soft_connections (c->id ()); + for (auto i = sc.begin (); i != sc.end (); ++i) { + if (! s.empty ()) { + s += "\n"; + } + s += "(#" + tl::to_string (*i) + "->#" + tl::to_string (c->id ()) + ")"; + } + } return s; } @@ -590,6 +679,90 @@ TEST(23_LocalClustersWithEdges) } } +TEST(24_LocalClustersWithSoftConnections) +{ + db::Layout layout; + db::Cell &cell = layout.cell (layout.add_cell ("TOP")); + db::GenericRepository &repo = layout.shape_repository (); + + auto dbu = db::CplxTrans (layout.dbu ()).inverted (); + + unsigned int nwell = 0; + unsigned int ntie = 1; + unsigned int ptie = 2; + unsigned int contact = 3; + unsigned int metal1 = 4; + + cell.shapes (nwell).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.0, 4.0, 2.0, 8.0)), repo)); + cell.shapes (ntie).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.5, 5.0, 1.5, 7.0)), repo)); + cell.shapes (contact).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.8, 6.0, 1.2, 6.5)), repo)); + cell.shapes (metal1).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.0, 5.0, 2.0, 7.0)), repo)); + + cell.shapes (ptie).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.5, 1.0, 1.5, 3.0)), repo)); + cell.shapes (contact).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.8, 2.0, 1.2, 2.5)), repo)); + cell.shapes (metal1).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.0, 1.0, 2.0, 3.0)), repo)); + + db::Connectivity conn; + conn.connect (nwell); + conn.connect (ntie); + conn.connect (ptie); + conn.connect (contact); + conn.connect (metal1); + conn.soft_connect (ntie, nwell); + conn.soft_connect (contact, ntie); + conn.connect (metal1, contact); + + { + db::local_clusters clusters; + clusters.build_clusters (cell, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,4000;0,8000;2000,8000;2000,4000)\n" + "#2:[1](500,5000;500,7000;1500,7000;1500,5000)\n" + "#3:[3](800,6000;800,6500;1200,6500;1200,6000);[4](0,5000;0,7000;2000,7000;2000,5000)\n" + "#4:[3](800,2000;800,2500;1200,2500;1200,2000);[4](0,1000;0,3000;2000,3000;2000,1000)\n" + "#5:[2](500,1000;500,3000;1500,3000;1500,1000)\n" + "(#2->#1)\n" + "(#3->#2)" + ); + } + + conn.soft_connect (contact, ptie); + + { + db::local_clusters clusters; + clusters.build_clusters (cell, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,4000;0,8000;2000,8000;2000,4000)\n" + "#2:[1](500,5000;500,7000;1500,7000;1500,5000)\n" + "#3:[3](800,6000;800,6500;1200,6500;1200,6000);[4](0,5000;0,7000;2000,7000;2000,5000)\n" + "#4:[2](500,1000;500,3000;1500,3000;1500,1000)\n" + "#5:[3](800,2000;800,2500;1200,2500;1200,2000);[4](0,1000;0,3000;2000,3000;2000,1000)\n" + "(#2->#1)\n" + "(#3->#2)\n" + "(#5->#4)" + ); + } + + conn.soft_connect_global (ptie, "BULK"); + + { + db::local_clusters clusters; + clusters.build_clusters (cell, conn); + EXPECT_EQ (local_clusters_to_string (clusters, conn), + "#1:[0](0,4000;0,8000;2000,8000;2000,4000)\n" + "#2:[1](500,5000;500,7000;1500,7000;1500,5000)\n" + "#3:[3](800,6000;800,6500;1200,6500;1200,6000);[4](0,5000;0,7000;2000,7000;2000,5000)\n" + "#4:[2](500,1000;500,3000;1500,3000;1500,1000)\n" + "#5:[3](800,2000;800,2500;1200,2500;1200,2000);[4](0,1000;0,3000;2000,3000;2000,1000)\n" + "#6:+BULK\n" + "(#2->#1)\n" + "(#3->#2)\n" + "(#5->#4)\n" + "(#4->#6)" + ); + } +} + TEST(30_LocalConnectedClusters) { db::Layout layout; @@ -920,7 +1093,7 @@ static void copy_cluster_shapes (const std::string *&attrs, db::Shapes &out, db: const db::local_cluster &lc = clusters.cluster_by_id (cluster_id); // copy the shapes from this cell - for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { + for (db::Connectivity::all_layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) { for (db::local_cluster::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) { db::Polygon poly = s->obj ().transformed (trans * db::ICplxTrans (s->trans ())); out.insert (db::PolygonWithProperties (poly, cell_and_attr_pid > 0 ? cell_and_attr_pid : cell_pid)); diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 916169102..0e2bf1c57 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -101,6 +101,11 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters< for (db::Net::const_terminal_iterator t = n->begin_terminals (); t != n->end_terminals (); ++t) { const db::NetTerminalRef &tref = *t; + + if (! tref.device ()->device_abstract ()) { + continue; + } + db::cell_index_type dci = tref.device ()->device_abstract ()->cell_index (); db::DCplxTrans dtr = tref.device ()->trans (); @@ -121,6 +126,10 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters< for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + if (! d->device_abstract ()) { + continue; + } + std::vector original_device_cells; db::cell_index_type dci = d->device_abstract ()->cell_index (); @@ -3176,6 +3185,334 @@ TEST(14_JoinNets) db::compare_layouts (_this, ly, au); } +static +void annotate_soft_connections (db::Netlist &netlist, const db::hier_clusters &net_clusters) +{ + db::DeviceClassDiode *soft_diode = new db::DeviceClassDiode (); + soft_diode->set_name ("SOFT"); + netlist.add_device_class (soft_diode); + + for (auto c = netlist.begin_bottom_up (); c != netlist.end_bottom_up (); ++c) { + + auto clusters = net_clusters.clusters_per_cell (c->cell_index ()); + + for (auto n = c->begin_nets (); n != c->end_nets (); ++n) { + + auto soft_connections = clusters.upward_soft_connections (n->cluster_id ()); + for (auto sc = soft_connections.begin (); sc != soft_connections.end (); ++sc) { + + db::Device *sc_device = new db::Device (soft_diode); + c->add_device (sc_device); + + auto nn = c->net_by_cluster_id (*sc); + if (nn) { + sc_device->connect_terminal (db::DeviceClassDiode::terminal_id_C, n.operator-> ()); + sc_device->connect_terminal (db::DeviceClassDiode::terminal_id_A, nn); + } + + } + + } + + } +} + +TEST(15_SoftConnections) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int diff = define_layer (ly, lmap, 2); + unsigned int pplus = define_layer (ly, lmap, 3); + unsigned int nplus = define_layer (ly, lmap, 4); + unsigned int poly = define_layer (ly, lmap, 5); + unsigned int contact = define_layer (ly, lmap, 8); + unsigned int metal1 = define_layer (ly, lmap, 9); + unsigned int via1 = define_layer (ly, lmap, 10); + unsigned int metal2 = define_layer (ly, lmap, 11); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testdata ()); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "soft_connections.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::DeepShapeStore dss; + dss.set_text_enlargement (1); + dss.set_text_property_name (tl::Variant ("LABEL")); + + // original layers + db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss); + db::Region rdiff (db::RecursiveShapeIterator (ly, tc, diff), dss); + db::Region rpplus (db::RecursiveShapeIterator (ly, tc, pplus), dss); + db::Region rnplus (db::RecursiveShapeIterator (ly, tc, nplus), dss); + db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss); + db::Region rcontact (db::RecursiveShapeIterator (ly, tc, contact), dss); + db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss); + db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss); + db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss); + + // derived regions + + db::Region rdiff_in_nwell = rdiff & rnwell; + db::Region rpdiff = rdiff_in_nwell - rnplus; + db::Region rntie = rdiff_in_nwell & rnplus; + db::Region rpgate = rpdiff & rpoly; + db::Region rpsd = rpdiff - rpgate; + + db::Region rdiff_outside_nwell = rdiff - rnwell; + db::Region rndiff = rdiff_outside_nwell - rpplus; + db::Region rptie = rdiff_outside_nwell & rpplus; + db::Region rngate = rndiff & rpoly; + db::Region rnsd = rndiff - rngate; + + // Global + + db::Region bulk (dss); + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lpgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate (p) + unsigned int lngate = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Gate (n) + unsigned int lpsd = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> Source/Drain (p) + unsigned int lnsd = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> Source/Drain (n) + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (15, 0)); // 15/0 -> N Diffusion + unsigned int lptie = ly.insert_layer (db::LayerProperties (16, 0)); // 16/0 -> P Tie + unsigned int lntie = ly.insert_layer (db::LayerProperties (17, 0)); // 17/0 -> N Tie + + rpgate.insert_into (&ly, tc.cell_index (), lpgate); + rngate.insert_into (&ly, tc.cell_index (), lngate); + rpsd.insert_into (&ly, tc.cell_index (), lpsd); + rnsd.insert_into (&ly, tc.cell_index (), lnsd); + rpdiff.insert_into (&ly, tc.cell_index (), lpdiff); + rndiff.insert_into (&ly, tc.cell_index (), lndiff); + rptie.insert_into (&ly, tc.cell_index (), lptie); + rntie.insert_into (&ly, tc.cell_index (), lntie); + + // perform the extraction + + db::Netlist nl; + db::hier_clusters cl; + + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["W"] = &rnwell; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + pmos_ex.extract (dss, 0, dl, nl, cl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["W"] = &bulk; + dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes + nmos_ex.extract (dss, 0, dl, nl, cl); + + // perform the net extraction + + db::NetlistExtractor net_ex; + + db::Connectivity conn; + + // Global nets + conn.connect_global (bulk, "BULK"); + conn.soft_connect_global (rptie, "BULK"); + + // Intra-layer + conn.connect (rpsd); + conn.connect (rnsd); + conn.connect (rptie); + conn.connect (rntie); + conn.connect (rnwell); + conn.connect (rpoly); + conn.connect (rcontact); + conn.connect (rmetal1); + conn.connect (rvia1); + conn.connect (rmetal2); + // Inter-layer + conn.soft_connect (rcontact, rpsd); + conn.soft_connect (rcontact, rnsd); + conn.soft_connect (rntie, rnwell); + conn.soft_connect (rcontact, rptie); + conn.soft_connect (rcontact, rntie); + conn.soft_connect (rcontact, rpoly); + conn.connect (rcontact, rmetal1); + conn.connect (rvia1, rmetal1); + conn.connect (rvia1, rmetal2); + + // extract the nets + std::list > jn; + + jn.push_back (std::set ()); + jn.back ().insert ("BULK"); + jn.back ().insert ("VSS"); + + jn.push_back (std::set ()); + jn.back ().insert ("NWELL"); + jn.back ().insert ("VDD"); + + net_ex.set_joined_nets ("INV2", jn); + + std::list gp; + gp.push_back (tl::GlobPattern ("NEXT")); + gp.push_back (tl::GlobPattern ("FB")); + net_ex.set_joined_net_names (gp); + + net_ex.extract_nets (dss, 0, conn, nl, cl); + + annotate_soft_connections (nl, cl); + + EXPECT_EQ (all_net_names_unique (nl), true); + + // debug layers produced for nets + std::map dump_map; + dump_map [layer_of (bulk) ] = ly.insert_layer (db::LayerProperties (200, 0)); + dump_map [layer_of (rnwell) ] = ly.insert_layer (db::LayerProperties (201, 0)); + dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (202, 0)); + dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [layer_of (rcontact) ] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); + dump_nets_to_layout (nl, cl, ly, dump_map, cm, true); + + // compare netlist as string + CHECKPOINT (); + db::compare_netlist (_this, nl, + "circuit RINGO ();\n" + " subcircuit ND2X1 $1 (NWELL=$4,B=FB,A=ENABLE,VDD=VDD,OUT=$5,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $2 (NWELL=$4,IN=$14,VDD=VDD,OUT=FB,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $3 (NWELL=$4,IN=FB,VDD=$I17,OUT=OUT,VSS=$I27,BULK=BULK);\n" + " subcircuit TIE $4 (NWELL=$4,VSS=$I27,VDD=$I17,BULK=BULK);\n" + " subcircuit EMPTY $5 ($1=$4,$2=$I27,$3=$I17);\n" + " subcircuit TIE $6 (NWELL=$4,VSS=VSS,VDD=VDD,BULK=BULK);\n" + " subcircuit INVX1 $7 (NWELL=$4,IN=$5,VDD=VDD,OUT=$6,VSS=VSS,BULK=BULK);\n" + " subcircuit EMPTY $8 ($1=$4,$2=VSS,$3=VDD);\n" + " subcircuit INVX1 $9 (NWELL=$4,IN=$6,VDD=VDD,OUT=$7,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $10 (NWELL=$4,IN=$7,VDD=VDD,OUT=$8,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $11 (NWELL=$4,IN=$8,VDD=VDD,OUT=$9,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $12 (NWELL=$4,IN=$9,VDD=VDD,OUT=$10,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $13 (NWELL=$4,IN=$10,VDD=VDD,OUT=$11,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $14 (NWELL=$4,IN=$11,VDD=VDD,OUT=$12,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $15 (NWELL=$4,IN=$12,VDD=VDD,OUT=$15,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $16 (NWELL=$4,IN=$15,VDD=VDD,OUT=$14,VSS=VSS,BULK=BULK);\n" + "end;\n" + "circuit ND2X1 (NWELL=NWELL,B=B,A=A,VDD=VDD,OUT=OUT,VSS=VSS,BULK=BULK);\n" + " device PMOS $1 (S=$7,G=$4,D=$9,B=NWELL) (L=0.25,W=1.5,AS=0.6375,AD=0.3375,PS=3.85,PD=1.95);\n" + " device PMOS $2 (S=$9,G=$2,D=$6,B=NWELL) (L=0.25,W=1.5,AS=0.3375,AD=0.6375,PS=1.95,PD=3.85);\n" + " device NMOS $3 (S=$13,G=$4,D=$14,B=BULK) (L=0.25,W=0.95,AS=0.40375,AD=0.21375,PS=2.75,PD=1.4);\n" + " device NMOS $4 (S=$14,G=$2,D=$11,B=BULK) (L=0.25,W=0.95,AS=0.21375,AD=0.40375,PS=1.4,PD=2.75);\n" + " device SOFT $5 (A=B,C=$2) (A=0,P=0);\n" + " device SOFT $6 (A=A,C=$4) (A=0,P=0);\n" + " device SOFT $7 (A=OUT,C=$6) (A=0,P=0);\n" + " device SOFT $8 (A=OUT,C=$7) (A=0,P=0);\n" + " device SOFT $9 (A=VDD,C=$9) (A=0,P=0);\n" + " device SOFT $10 (A=OUT,C=$11) (A=0,P=0);\n" + " device SOFT $11 (A=VSS,C=$13) (A=0,P=0);\n" + "end;\n" + "circuit TIE (NWELL=NWELL,VSS=VSS,VDD=VDD,BULK=BULK);\n" + " device SOFT $1 (A=$2,C=NWELL) (A=0,P=0);\n" + " device SOFT $2 (A=VDD,C=$2) (A=0,P=0);\n" + " device SOFT $3 (A=VSS,C=$3) (A=0,P=0);\n" + " device SOFT $4 (A=$3,C=BULK) (A=0,P=0);\n" + "end;\n" + "circuit EMPTY ($1=$1,$2=$2,$3=$3);\n" + "end;\n" + "circuit INVX1 (NWELL=NWELL,IN=IN,VDD=VDD,OUT=OUT,VSS=VSS,BULK=BULK);\n" + " device PMOS $1 (S=$5,G=$2,D=$7,B=NWELL) (L=0.25,W=1.5,AS=0.6375,AD=0.6375,PS=3.85,PD=3.85);\n" + " device NMOS $2 (S=$10,G=$2,D=$8,B=BULK) (L=0.25,W=0.95,AS=0.40375,AD=0.40375,PS=2.75,PD=2.75);\n" + " device SOFT $3 (A=IN,C=$2) (A=0,P=0);\n" + " device SOFT $4 (A=VDD,C=$5) (A=0,P=0);\n" + " device SOFT $5 (A=OUT,C=$7) (A=0,P=0);\n" + " device SOFT $6 (A=OUT,C=$8) (A=0,P=0);\n" + " device SOFT $7 (A=VSS,C=$10) (A=0,P=0);\n" + "end;\n" + ); + + // doesn't do anything here, but we test that this does not destroy anything: + nl.combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + nl.make_top_level_pins (); + nl.purge (); + + // compare netlist as string + CHECKPOINT (); + db::compare_netlist (_this, nl, + "circuit RINGO (FB=FB,ENABLE=ENABLE,OUT=OUT,VDD=VDD,VSS=VSS,BULK=BULK);\n" + " subcircuit ND2X1 $1 (NWELL=$4,B=FB,A=ENABLE,VDD=VDD,OUT=$5,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $2 (NWELL=$4,IN=$14,VDD=VDD,OUT=FB,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $3 (NWELL=$4,IN=FB,VDD=$I17,OUT=OUT,VSS=$I27,BULK=BULK);\n" + " subcircuit TIE $4 (NWELL=$4,VSS=$I27,VDD=$I17,BULK=BULK);\n" + " subcircuit TIE $5 (NWELL=$4,VSS=VSS,VDD=VDD,BULK=BULK);\n" + " subcircuit INVX1 $6 (NWELL=$4,IN=$5,VDD=VDD,OUT=$6,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $7 (NWELL=$4,IN=$6,VDD=VDD,OUT=$7,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $8 (NWELL=$4,IN=$7,VDD=VDD,OUT=$8,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $9 (NWELL=$4,IN=$8,VDD=VDD,OUT=$9,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $10 (NWELL=$4,IN=$9,VDD=VDD,OUT=$10,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $11 (NWELL=$4,IN=$10,VDD=VDD,OUT=$11,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $12 (NWELL=$4,IN=$11,VDD=VDD,OUT=$12,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $13 (NWELL=$4,IN=$12,VDD=VDD,OUT=$15,VSS=VSS,BULK=BULK);\n" + " subcircuit INVX1 $14 (NWELL=$4,IN=$15,VDD=VDD,OUT=$14,VSS=VSS,BULK=BULK);\n" + "end;\n" + "circuit ND2X1 (NWELL=NWELL,B=B,A=A,VDD=VDD,OUT=OUT,VSS=VSS,BULK=BULK);\n" + " device PMOS $1 (S=$7,G=$4,D=$9,B=NWELL) (L=0.25,W=1.5,AS=0.6375,AD=0.3375,PS=3.85,PD=1.95);\n" + " device PMOS $2 (S=$9,G=$2,D=$6,B=NWELL) (L=0.25,W=1.5,AS=0.3375,AD=0.6375,PS=1.95,PD=3.85);\n" + " device NMOS $3 (S=$13,G=$4,D=$14,B=BULK) (L=0.25,W=0.95,AS=0.40375,AD=0.21375,PS=2.75,PD=1.4);\n" + " device NMOS $4 (S=$14,G=$2,D=$11,B=BULK) (L=0.25,W=0.95,AS=0.21375,AD=0.40375,PS=1.4,PD=2.75);\n" + " device SOFT $5 (A=B,C=$2) (A=0,P=0);\n" + " device SOFT $6 (A=A,C=$4) (A=0,P=0);\n" + " device SOFT $7 (A=OUT,C=$6) (A=0,P=0);\n" + " device SOFT $8 (A=OUT,C=$7) (A=0,P=0);\n" + " device SOFT $9 (A=VDD,C=$9) (A=0,P=0);\n" + " device SOFT $10 (A=OUT,C=$11) (A=0,P=0);\n" + " device SOFT $11 (A=VSS,C=$13) (A=0,P=0);\n" + "end;\n" + "circuit TIE (NWELL=NWELL,VSS=VSS,VDD=VDD,BULK=BULK);\n" + " device SOFT $1 (A=$2,C=NWELL) (A=0,P=0);\n" + " device SOFT $2 (A=VDD,C=$2) (A=0,P=0);\n" + " device SOFT $3 (A=VSS,C=$3) (A=0,P=0);\n" + " device SOFT $4 (A=$3,C=BULK) (A=0,P=0);\n" + "end;\n" + "circuit INVX1 (NWELL=NWELL,IN=IN,VDD=VDD,OUT=OUT,VSS=VSS,BULK=BULK);\n" + " device PMOS $1 (S=$5,G=$2,D=$7,B=NWELL) (L=0.25,W=1.5,AS=0.6375,AD=0.6375,PS=3.85,PD=3.85);\n" + " device NMOS $2 (S=$10,G=$2,D=$8,B=BULK) (L=0.25,W=0.95,AS=0.40375,AD=0.40375,PS=2.75,PD=2.75);\n" + " device SOFT $3 (A=IN,C=$2) (A=0,P=0);\n" + " device SOFT $4 (A=VDD,C=$5) (A=0,P=0);\n" + " device SOFT $5 (A=OUT,C=$7) (A=0,P=0);\n" + " device SOFT $6 (A=OUT,C=$8) (A=0,P=0);\n" + " device SOFT $7 (A=VSS,C=$10) (A=0,P=0);\n" + "end;\n" + ); + + // compare the collected test data + + std::string au = tl::testdata (); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "soft_connections_au.gds"); + + db::compare_layouts (_this, ly, au); +} + TEST(100_issue954) { diff --git a/src/doc/doc/about/drc_ref_global.xml b/src/doc/doc/about/drc_ref_global.xml index 670651628..45b725686 100644 --- a/src/doc/doc/about/drc_ref_global.xml +++ b/src/doc/doc/about/drc_ref_global.xml @@ -1838,6 +1838,36 @@ is equivalent to "layer.smoothed" (see DRC expressions (see Layer#drc and smoothed for more details).

+

"soft_connect" - Specifies a soft connection between two layers

+ +

Usage:

+
    +
  • soft_connect(a, b)
  • +
+

+A "soft connection" is made between two layers and +is a directional connection (like an ideal diode). +Soft connections allow detecting if nets are connected +via a high-ohmic substrate or diffusion layer (the +"lower" layer). +"b" is the "lower" and "a" the upper layer. +

+See Netter#connect for a more detailed description of that function. +

+

"soft_connect_global" - Specifies a soft connection to a global net

+ +

Usage:

+
    +
  • soft_connect_global(l, name)
  • +
+

+Like soft_connect, a soft connection is made between +a layer and a global net (e.g. substrate). The global net +is always the "lower" net of the soft connection. +

+See Netter#soft_connect_global for a more detailed +description of that function. +

"source" - Specifies a source layout

Usage:

diff --git a/src/doc/doc/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml index 8fd41f6f5..bd82b4fbd 100644 --- a/src/doc/doc/about/drc_ref_netter.xml +++ b/src/doc/doc/about/drc_ref_netter.xml @@ -434,6 +434,49 @@ layout analysis. Hence, all
connect, connect statements will clear the netlist and re-extract it again.

+

"soft_connect" - Specifies a soft connection between two layers

+ +

Usage:

+
    +
  • soft_connect(a, b)
  • +
+

+a and b must be polygon or text layers. After calling this function, the +Netter considers shapes from layer a and b connected in "soft mode". +Typically, b is a high-ohmic layer such as diffusion, implant for substate +material, also called the "lower" layer. +

+A soft connection between shapes from layer a and b forms a directional +connection like an ideal diode: current can flow down, but now up +(not meant in the physical sense, this is a concept). +

+Hence, two nets are disconnected, if they both connect to the same lower layer, +but do not have a connection between them. +

+The netlist extractor will use this scheme to identify nets that are +connected only via such a high-ohmic region. Such a case is typically +bad for the functionality of a device and reported as an error. +Once, the check has been made and no error is found, soft-connected +nets are joined the same way than hard connections are made. +

+Beside this, soft connections follow the same rules than hard connections +(see connect). +

+

"soft_connect_global" - Soft-connects a layer with a global net

+ +

Usage:

+
    +
  • soft-connect_global(l, name)
  • +
+

+Connects the shapes from the given layer l to a global net with the given name +in "soft mode". +

+See connect_global for details about the concepts of global nets. +See soft_connect for details about the concept of soft connections. +In global net soft connections, the global net (typically a substrate) +is always the "lower" layer. +

"top_level" - Specifies top level mode

Usage:

diff --git a/src/doc/doc/manual/lvs_connect.xml b/src/doc/doc/manual/lvs_connect.xml index 501695d35..73e0cb454 100644 --- a/src/doc/doc/manual/lvs_connect.xml +++ b/src/doc/doc/manual/lvs_connect.xml @@ -10,6 +10,7 @@

Intra- and inter-layer connections

+

The connectivity setup of a LVS script determines how the connections are made. @@ -77,6 +78,7 @@ connect(metal2, metal2_labels)

Global connections

+

KLayout supports implicit connections made across all polygons on @@ -98,6 +100,7 @@ connect(metal2, metal2_labels)

Implicit connections

+

Implicit connections can be useful to supply preliminary connections @@ -193,6 +196,7 @@ connect(metal2, metal2_labels)

Explicit connections

+

Explicit connections can be useful to enforce a connection in the layout @@ -280,4 +284,219 @@ connect_explicit("INV", [ "BULK", "VSS" ]) statements.

+

Soft connections

+ + + +

+ Soft connections are a way to find wiring issues where signals + or even power is routed over high-ohmic paths. + High-ohmic paths can be established through connections via + poly silicon, implant, well or substrate areas. Such areas + can easily show resistance values which are a hundred times + higher than that of metal connections. We have to make sure + that for routing power or critical signals, connections are + not made through such areas, but primarily through metal + connections. +

+ +

+ Here is an example: +

+ +

+ +

+ +

+ In this case, we have a standard textbook planar CMOS technology with two PMOS devices + sitting in a n-well. These could be the two PMOS of two inverter pairs for example. + Both PMOS need to be connected to VDD at their sources. In addition, the n-well + area also needs to be tied to VDD in order to provide reverse bias for the p+ drain + areas and the body potential for the transistors, forming the opposite + electrode of the gate capacity. +

+ +

Such a technology stack can be described by the following connectivity:

+ +
# Input layers
+
+nwell = ...
+active = ...
+pplus = ...
+nplus = ...
+poly = ...
+contact = ...
+metal1 = ...
+via1 = ...
+metal2 = ...
+
+# computed layers 
+
+(nactive, pactive) = active.and_not(nwell)
+
+# PMOS and NMOS source/drain regions
+psd  = (nactive & pplus) - poly
+nsd  = (pactive & nplus) - poly
+
+# n tie and p tie (nwell and substrate contact)
+ntie = nactive & nplus
+ptie = pactive & pplus
+
+# connections
+
+# nwell connections
+connect(ntie, nwell) 
+connect(contact, ntie) 
+
+# substrate connections
+connect_global(ptie, "BULK")
+connect(contact, ptie)
+
+# device connections
+connect(contact, psd) 
+connect(contact, nsd) 
+connect(contact, poly)
+
+# metal connections
+connect(metal1, contact)
+connect(via1, metal1)
+connect(metal2, via1)
+
+ +

+ However, there is an issue: + As shown in the picture, the left PMOS source is properly connected to VDD. + The right one however lacks the metal connection to VDD. From the perspective + of pure connectivity, this transistor's source is connected to VDD, but only + through a weak n-well connection. Such a device will not work - or at least, badly. +

+ +

+ The solution is to introduce soft connections. Soft connections are made + by replacing "connect" with "soft_connect" and "connect_global" with "soft_connect_global" + (see soft_connect and + soft_connect_global). + The first layer is the "upper" layer while the second layer is the "lower" layer. + The lower layer is the high-ohmic one. In global connections, the global net is + always the high-ohmic one. +

+ +

+ Soft connections can be visualized as directional connections: current can only flow from the + upper to the lower layer, but not the other way. So, a real connection is only made, + if both upper terminals of the soft connections are connected to the same physical net. +

+ +

+ To solve the n-well issue we have to substitute the n-tie to n-well connection statement + by a "soft_connect" statement: +

+ +
soft_connect(ntie, nwell)
+ +

+ The above picture now looks like this: +

+ +

+ +

+ +

+ With this definition, the netlist extractor is able to detect the fault + and raise a warning or an error (in top level mode). + The warning is shown on the log tab and indicates incomplete wiring plus + details about the subnets, separated by the soft connections. +

+ +

+ The complete the setup we also need to include other soft connections, + such as connections via substrate (a global soft connect to the BULK net), + via source/drain implants, via the tie implants and via poly: +

+ +
# Input layers
+
+nwell = ...
+active = ...
+pplus = ...
+nplus = ...
+poly = ...
+contact = ...
+metal1 = ...
+via1 = ...
+metal2 = ...
+
+# computed layers 
+
+(nactive, pactive) = active.and_not(nwell)
+
+# PMOS and NMOS source/drain regions
+psd  = (nactive & pplus) - poly
+nsd  = (pactive & nplus) - poly
+
+# n tie and p tie (nwell and substrate contact)
+ntie = nactive & nplus
+ptie = pactive & pplus
+
+# connections
+
+# nwell connections
+soft_connect(ntie, nwell) 
+soft_connect(contact, ntie) 
+
+# substrate connections
+soft_connect_global(ptie, "BULK")
+soft_connect(contact, ptie)
+
+# device connections
+soft_connect(contact, psd) 
+soft_connect(contact, nsd) 
+soft_connect(contact, poly)
+
+# metal connections
+connect(metal1, contact)
+connect(via1, metal1)
+connect(metal2, via1)
+
+ +

+ As this code demonstrates, multiple soft connections can be specified. + From the perspective of the check, all soft connections are of the same kind. +

+ +

+ Note, that two opposite soft connections cancel, so this would eventually + make a hard connection: +

+ +
soft_connect(a, b)
+soft_connect(b, a)
+
+ +

+ NOTE: It is therefore important to observe the direction of soft connections: + upper and high-conductive / low-ohmic layer first, and lower and low-conductive / high-ohmic + layer second. +

+ +

Soft connections and "must connect" nets

+ +

+ Soft connections and must connect nets (aka "connect_explicit" and "connect_implicit") + serve the same purpose - to detect incomplete wiring. Typically they are used + together, such as doing "connect_implicit" on VDD nets and "connect_explicit" + on VDD and NWELL nets if the schematic circuits do not feature an explicit + NWELL pin. +

+ +

+ Soft connections are checked before connect_explicit and connect_implicit + are executed. This means, that soft connection errors cannot be masked by + declaring them "must connect". On the other hand, that is not a real issue + as both checks would raise an warning or error (in the top-level case). + It would only be a different kind of warning. +

+ diff --git a/src/doc/doc/manual/soft_connections.png b/src/doc/doc/manual/soft_connections.png new file mode 100644 index 000000000..a13e6f93b Binary files /dev/null and b/src/doc/doc/manual/soft_connections.png differ diff --git a/src/doc/doc/manual/soft_connections2.png b/src/doc/doc/manual/soft_connections2.png new file mode 100644 index 000000000..cf0ff5cd0 Binary files /dev/null and b/src/doc/doc/manual/soft_connections2.png differ diff --git a/src/doc/docResources.qrc b/src/doc/docResources.qrc index a853c8b3d..c0e4c856a 100644 --- a/src/doc/docResources.qrc +++ b/src/doc/docResources.qrc @@ -244,6 +244,8 @@ doc/images/drc_raw1.png doc/images/drc_raw2.png doc/images/drc_raw3.png + doc/manual/soft_connections.png + doc/manual/soft_connections2.png doc/programming/introduction.xml diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index e5f2ffa26..81e325ff3 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -2284,12 +2284,36 @@ CODE # @synopsis connect(a, b) # See \Netter#connect for a description of that function. + # %DRC% + # @name soft_connect + # @brief Specifies a soft connection between two layers + # @synopsis soft_connect(a, b) + # A "soft connection" is made between two layers and + # is a directional connection (like an ideal diode). + # Soft connections allow detecting if nets are connected + # via a high-ohmic substrate or diffusion layer (the + # "lower" layer). + # "b" is the "lower" and "a" the upper layer. + # + # See \Netter#connect for a more detailed description of that function. + # %DRC% # @name connect_global # @brief Specifies a connection to a global net # @synopsis connect_global(l, name) # See \Netter#connect_global for a description of that function. + # %DRC% + # @name soft_connect_global + # @brief Specifies a soft connection to a global net + # @synopsis soft_connect_global(l, name) + # Like \soft_connect, a soft connection is made between + # a layer and a global net (e.g. substrate). The global net + # is always the "lower" net of the soft connection. + # + # See \Netter#soft_connect_global for a more detailed + # description of that function. + # %DRC% # @name clear_connections # @brief Clears all connections stored so far @@ -2360,14 +2384,18 @@ CODE clear_connections connect connect_global + soft_connect + soft_connect_global connect_implicit connect_explicit device_scaling top_level ignore_extraction_errors extract_devices - l2n_data netlist + l2n_data + _l2n_object + _make_soft_connection_diodes ).each do |f| eval <<"CODE" def #{f}(*args) diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 8c970483c..627dd85a9 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -116,6 +116,51 @@ module DRC end + # %DRC% + # @name soft_connect + # @brief Specifies a soft connection between two layers + # @synopsis soft_connect(a, b) + # a and b must be polygon or text layers. After calling this function, the + # Netter considers shapes from layer a and b connected in "soft mode". + # Typically, b is a high-ohmic layer such as diffusion, implant for substate + # material, also called the "lower" layer. + # + # A soft connection between shapes from layer a and b forms a directional + # connection like an ideal diode: current can flow down, but now up + # (not meant in the physical sense, this is a concept). + # + # Hence, two nets are disconnected, if they both connect to the same lower layer, + # but do not have a connection between them. + # + # The netlist extractor will use this scheme to identify nets that are + # connected only via such a high-ohmic region. Such a case is typically + # bad for the functionality of a device and reported as an error. + # Once, the check has been made and no error is found, soft-connected + # nets are joined the same way than hard connections are made. + # + # Beside this, soft connections follow the same rules than hard connections + # (see \connect). + + def soft_connect(a, b) + + @engine._context("soft_connect") do + + a.is_a?(DRC::DRCLayer) || raise("First argument must be a layer") + b.is_a?(DRC::DRCLayer) || raise("Second argument must be a layer") + a.requires_texts_or_region + b.requires_texts_or_region + + register_layer(a.data) + register_layer(b.data) + # soft connections imply hard intra-layer connections + a.data.is_a?(RBA::Region) && @l2n.connect(a.data) + b.data.is_a?(RBA::Region) && @l2n.connect(b.data) + @l2n.soft_connect(a.data, b.data) + + end + + end + # %DRC% # @name connect_global # @brief Connects a layer with a global net @@ -140,6 +185,33 @@ module DRC end + # %DRC% + # @name soft_connect_global + # @brief Soft-connects a layer with a global net + # @synopsis soft-connect_global(l, name) + # Connects the shapes from the given layer l to a global net with the given name + # in "soft mode". + # + # See \connect_global for details about the concepts of global nets. + # See \soft_connect for details about the concept of soft connections. + # In global net soft connections, the global net (typically a substrate) + # is always the "lower" layer. + + def soft_connect_global(l, name) + + @engine._context("soft_connect_global") do + + l.is_a?(DRC::DRCLayer) || raise("Layer argument must be a layer") + l.requires_texts_or_region + + register_layer(l.data) + l.data.is_a?(RBA::Region) && @l2n.connect(l.data) + @l2n.soft_connect_global(l.data, name) + + end + + end + # %DRC% # @name extract_devices # @brief Extracts devices based on the given extractor class, name and device layer selection @@ -681,6 +753,14 @@ module DRC @l2n && @l2n.is_extracted? && self.l2n_data end + def _l2n_object + @l2n + end + + def _make_soft_connection_diodes(f) + @l2n.make_soft_connection_diodes = f + end + private def cleanup diff --git a/src/lay/lay/layHelpSource.cc b/src/lay/lay/layHelpSource.cc index e7b442735..9edce626a 100644 --- a/src/lay/lay/layHelpSource.cc +++ b/src/lay/lay/layHelpSource.cc @@ -519,7 +519,7 @@ HelpSource::get_image (const std::string &u) { QResource res (resource_url (QUrl::fromEncoded (u.c_str ()).path ())); if (res.size () == 0) { - throw tl::Exception (tl::to_string (QObject::tr ("ERROR: no data found for resource ")) + u); + throw tl::Exception (tl::to_string (QObject::tr ("No data found for resource ")) + u); } QByteArray data; @@ -552,7 +552,7 @@ HelpSource::get_css (const std::string &u) QResource res (resource_url (QUrl::fromEncoded (u.c_str ()).path ())); if (res.size () == 0) { - throw tl::Exception (tl::to_string (QObject::tr ("ERROR: no data found for resource ")) + u); + throw tl::Exception (tl::to_string (QObject::tr ("No data found for resource ")) + u); } QByteArray data; diff --git a/src/lay/lay/layResourceHelpProvider.cc b/src/lay/lay/layResourceHelpProvider.cc index 4e5be5cf3..21c50136b 100644 --- a/src/lay/lay/layResourceHelpProvider.cc +++ b/src/lay/lay/layResourceHelpProvider.cc @@ -54,7 +54,7 @@ ResourceHelpProvider::get (lay::HelpSource * /*src*/, const std::string &path) c QString qpath = tl::to_qstring (path); QResource res (resource_url (qpath)); if (res.size () == 0) { - throw tl::Exception (tl::to_string (QObject::tr ("ERROR: no data found for resource ")) + tl::to_string (res.fileName ())); + throw tl::Exception (tl::to_string (QObject::tr ("No data found for resource ")) + tl::to_string (res.fileName ())); } QByteArray data; diff --git a/src/layui/layui/NetlistBrowserPage.ui b/src/layui/layui/NetlistBrowserPage.ui index 88687f10e..57a2e16e0 100644 --- a/src/layui/layui/NetlistBrowserPage.ui +++ b/src/layui/layui/NetlistBrowserPage.ui @@ -134,7 +134,7 @@ ... - + :/run_16px.png:/run_16px.png @@ -576,6 +576,9 @@ Qt::ActionsContextMenu + + QAbstractItemView::ExtendedSelection + true diff --git a/src/layui/layui/layNetInfoDialog.cc b/src/layui/layui/layNetInfoDialog.cc index 8ab5ff6ff..be7fdb467 100644 --- a/src/layui/layui/layNetInfoDialog.cc +++ b/src/layui/layui/layNetInfoDialog.cc @@ -100,7 +100,7 @@ size_t count_shapes (db::LayoutToNetlist *l2ndb, db::Net *net) size_t n = 0; const db::Connectivity &conn = l2ndb->connectivity (); - for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { + for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { n += count_shapes (l2ndb, net, *layer); } @@ -276,7 +276,7 @@ void NetInfoDialog::update_info_text () bool incomplete = false; const db::Connectivity &conn = mp_l2ndb->connectivity (); - for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { + for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { std::string l = layer_string (mp_l2ndb.get (), *layer); diff --git a/src/layui/layui/layNetlistBrowserDialog.cc b/src/layui/layui/layNetlistBrowserDialog.cc index eb860859a..d8eeeded6 100644 --- a/src/layui/layui/layNetlistBrowserDialog.cc +++ b/src/layui/layui/layNetlistBrowserDialog.cc @@ -306,7 +306,7 @@ NetlistBrowserDialog::probe_net (const db::DPoint &p, bool trace_path) std::vector regions; const db::Connectivity &conn = l2ndb->connectivity (); - for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { + for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { db::LayerProperties lp = l2ndb->internal_layout ()->get_properties (*layer); if (! lp.is_null ()) { db::Region *region = l2ndb->layer_by_index (*layer); diff --git a/src/layui/layui/layNetlistBrowserPage.cc b/src/layui/layui/layNetlistBrowserPage.cc index d8a27bcf8..086dc39bb 100644 --- a/src/layui/layui/layNetlistBrowserPage.cc +++ b/src/layui/layui/layNetlistBrowserPage.cc @@ -235,6 +235,8 @@ NetlistBrowserPage::NetlistBrowserPage (QWidget * /*parent*/) connect (actionExportAll, SIGNAL (triggered ()), this, SLOT (export_all ())); connect (actionExportSelected, SIGNAL (triggered ()), this, SLOT (export_selected ())); + connect (mode_tab, SIGNAL (currentChanged (int)), this, SLOT (mode_tab_changed (int))); + forward->setEnabled (false); backward->setEnabled (false); } @@ -330,6 +332,13 @@ NetlistBrowserPage::eventFilter (QObject *watched, QEvent *event) } } +void +NetlistBrowserPage::mode_tab_changed (int) +{ + clear_highlights (); + dm_update_highlights (); +} + void NetlistBrowserPage::layer_list_changed (int) { @@ -1072,11 +1081,10 @@ NetlistBrowserPage::set_db (db::LayoutToNetlist *l2ndb) mode_tab->setTabEnabled (0, true); mode_tab->setTabEnabled (1, is_lvsdb); mode_tab->setTabEnabled (2, is_lvsdb); - mode_tab->setTabEnabled (3, is_lvsdb); + mode_tab->setTabEnabled (3, true); #if QT_VERSION >= 0x50F00 mode_tab->setTabVisible (1, is_lvsdb); mode_tab->setTabVisible (2, is_lvsdb); - mode_tab->setTabVisible (3, is_lvsdb); #endif if (is_lvsdb) { @@ -1166,7 +1174,7 @@ NetlistBrowserPage::setup_trees () if ((lvsdb && lvsdb->cross_ref ()) || (l2ndb && ! l2ndb->log_entries ().empty ())) { - NetlistLogModel *new_model = new NetlistLogModel (log_view, lvsdb->cross_ref (), l2ndb); + NetlistLogModel *new_model = new NetlistLogModel (log_view, lvsdb ? lvsdb->cross_ref () : 0, l2ndb); delete log_view->model (); log_view->setModel (new_model); @@ -1421,7 +1429,7 @@ NetlistBrowserPage::adjust_view () size_t cluster_id = net->cluster_id (); const db::Connectivity &conn = mp_database->connectivity (); - for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { + for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { db::Box layer_bbox; db::recursive_cluster_shape_iterator shapes (mp_database->net_clusters (), *layer, cell_index, cluster_id); @@ -1580,7 +1588,7 @@ NetlistBrowserPage::produce_highlights_for_net (const db::Net *net, size_t &n_ma tl::Color fallback_color = make_valid_color (m_colorizer.marker_color ()); const db::Connectivity &conn = mp_database->connectivity (); - for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { + for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { db::LayerProperties lp = layout->get_properties (*layer); std::map::const_iterator display = display_by_lp.find (lp); diff --git a/src/layui/layui/layNetlistBrowserPage.h b/src/layui/layui/layNetlistBrowserPage.h index 5b7a057e4..b051e30f2 100644 --- a/src/layui/layui/layNetlistBrowserPage.h +++ b/src/layui/layui/layNetlistBrowserPage.h @@ -214,6 +214,7 @@ private slots: void log_selection_changed (); void browse_color_for_net (); void select_color_for_net (); + void mode_tab_changed (int); protected: bool eventFilter (QObject *watched, QEvent *event); diff --git a/src/layui/layui/layNetlistCrossReferenceModel.cc b/src/layui/layui/layNetlistCrossReferenceModel.cc index c37ed5691..9463d3989 100644 --- a/src/layui/layui/layNetlistCrossReferenceModel.cc +++ b/src/layui/layui/layNetlistCrossReferenceModel.cc @@ -29,7 +29,7 @@ namespace lay static void build_top_circuit_list (const db::NetlistCrossReference *cross_ref, std::vector &top_level_circuits) { - if (! top_level_circuits.empty ()) { + if (! top_level_circuits.empty () || ! cross_ref) { return; } @@ -139,48 +139,80 @@ size_t NetlistCrossReferenceModel::top_circuit_count () const size_t NetlistCrossReferenceModel::child_circuit_count (const circuit_pair &circuits) const { + if (! mp_cross_ref.get ()) { + return 0; + } + build_child_circuit_map (mp_cross_ref.get (), m_child_circuits); return m_child_circuits [circuits].size (); } size_t NetlistCrossReferenceModel::net_count (const circuit_pair &circuits) const { + if (! mp_cross_ref.get ()) { + return 0; + } + const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits); return data ? data->nets.size () : 0; } size_t NetlistCrossReferenceModel::net_terminal_count (const net_pair &nets) const { + if (! mp_cross_ref.get ()) { + return 0; + } + const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets); return data ? data->terminals.size () : 0; } size_t NetlistCrossReferenceModel::net_subcircuit_pin_count (const net_pair &nets) const { + if (! mp_cross_ref.get ()) { + return 0; + } + const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets); return data ? data->subcircuit_pins.size () : 0; } size_t NetlistCrossReferenceModel::net_pin_count (const net_pair &nets) const { + if (! mp_cross_ref.get ()) { + return 0; + } + const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets); return data ? data->pins.size () : 0; } size_t NetlistCrossReferenceModel::device_count (const circuit_pair &circuits) const { + if (! mp_cross_ref.get ()) { + return 0; + } + const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits); return data ? data->devices.size () : 0; } size_t NetlistCrossReferenceModel::pin_count (const circuit_pair &circuits) const { + if (! mp_cross_ref.get ()) { + return 0; + } + const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits); return data ? data->pins.size () : 0; } size_t NetlistCrossReferenceModel::subcircuit_count (const circuit_pair &circuits) const { + if (! mp_cross_ref.get ()) { + return 0; + } + const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits); return data ? data->subcircuits.size () : 0; } @@ -218,6 +250,10 @@ struct DataGetter template static IndexedNetlistModel::circuit_pair get_parent_of (const Pair &pair, const db::NetlistCrossReference *cross_ref, std::map &cache) { + if (! cross_ref) { + return IndexedNetlistModel::circuit_pair ((const db::Circuit *) 0, (const db::Circuit *) 0); + } + typename std::map::iterator i = cache.find (pair); if (i == cache.end ()) { @@ -266,6 +302,7 @@ IndexedNetlistModel::circuit_pair NetlistCrossReferenceModel::parent_of (const I std::pair > NetlistCrossReferenceModel::top_circuit_from_index (size_t index) const { + tl_assert (mp_cross_ref.get ()); build_top_circuit_list (mp_cross_ref.get (), m_top_level_circuits); IndexedNetlistModel::circuit_pair cp = m_top_level_circuits [index]; @@ -276,6 +313,7 @@ std::pair > NetlistCrossReferenceModel::child_circuit_from_index (const circuit_pair &circuits, size_t index) const { + tl_assert (mp_cross_ref.get ()); build_child_circuit_map (mp_cross_ref.get (), m_child_circuits); IndexedNetlistModel::circuit_pair cp = m_child_circuits [circuits][index]; @@ -286,6 +324,7 @@ std::pair > NetlistCrossReferenceModel::circuit_from_index (size_t index) const { + tl_assert (mp_cross_ref.get ()); IndexedNetlistModel::circuit_pair cp = mp_cross_ref->begin_circuits () [index]; const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (cp); tl_assert (data != 0); @@ -294,6 +333,7 @@ std::pair > NetlistCrossReferenceModel::net_from_index (const circuit_pair &circuits, size_t index) const { + tl_assert (mp_cross_ref.get ()); const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits); tl_assert (data != 0); return std::make_pair (data->nets [index].pair, std::make_pair (data->nets [index].status, data->nets [index].msg)); @@ -301,11 +341,13 @@ std::pairother_net_for (first); } const db::Circuit *NetlistCrossReferenceModel::second_circuit_for (const db::Circuit *first) const { + tl_assert (mp_cross_ref.get ()); return mp_cross_ref->other_circuit_for (first); } @@ -382,7 +424,7 @@ namespace { void NetlistCrossReferenceModel::ensure_subcircuit_data_built () const { - if (! m_per_subcircuit_data.empty ()) { + if (! m_per_subcircuit_data.empty () || ! mp_cross_ref.get ()) { return; } @@ -466,6 +508,7 @@ IndexedNetlistModel::net_subcircuit_pin_pair NetlistCrossReferenceModel::subcirc IndexedNetlistModel::net_subcircuit_pin_pair NetlistCrossReferenceModel::net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const { + tl_assert (mp_cross_ref.get ()); const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets); tl_assert (data != 0); return data->subcircuit_pins [index]; @@ -473,6 +516,7 @@ IndexedNetlistModel::net_subcircuit_pin_pair NetlistCrossReferenceModel::net_sub IndexedNetlistModel::net_terminal_pair NetlistCrossReferenceModel::net_terminalref_from_index (const net_pair &nets, size_t index) const { + tl_assert (mp_cross_ref.get ()); const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets); tl_assert (data != 0); return data->terminals [index]; @@ -480,6 +524,7 @@ IndexedNetlistModel::net_terminal_pair NetlistCrossReferenceModel::net_terminalr IndexedNetlistModel::net_pin_pair NetlistCrossReferenceModel::net_pinref_from_index (const net_pair &nets, size_t index) const { + tl_assert (mp_cross_ref.get ()); const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets); tl_assert (data != 0); return data->pins [index]; @@ -487,6 +532,7 @@ IndexedNetlistModel::net_pin_pair NetlistCrossReferenceModel::net_pinref_from_in std::pair > NetlistCrossReferenceModel::device_from_index (const circuit_pair &circuits, size_t index) const { + tl_assert (mp_cross_ref.get ()); const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits); tl_assert (data != 0); return std::make_pair (data->devices [index].pair, std::make_pair (data->devices [index].status, data->devices [index].msg)); @@ -494,6 +540,7 @@ std::pair > NetlistCrossReferenceModel::pin_from_index (const circuit_pair &circuits, size_t index) const { + tl_assert (mp_cross_ref.get ()); const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits); tl_assert (data != 0); return std::make_pair (data->pins [index].pair, std::make_pair (data->pins [index].status, data->pins [index].msg)); @@ -501,6 +548,7 @@ std::pair > NetlistCrossReferenceModel::subcircuit_from_index (const circuit_pair &circuits, size_t index) const { + tl_assert (mp_cross_ref.get ()); const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits); tl_assert (data != 0); return std::make_pair (data->subcircuits [index].pair, std::make_pair (data->subcircuits [index].status, data->subcircuits [index].msg)); @@ -536,6 +584,10 @@ static size_t get_index_of (const Pair &pair, Iter begin, Iter end, std::map::iterator i = m_index_of_circuits.find (circuits); if (i == m_index_of_circuits.end ()) { @@ -562,6 +614,10 @@ size_t NetlistCrossReferenceModel::circuit_index (const circuit_pair &circuits) size_t NetlistCrossReferenceModel::net_index (const net_pair &nets) const { + if (! mp_cross_ref.get ()) { + return lay::no_netlist_index; + } + circuit_pair circuits = parent_of (nets); const db::NetlistCrossReference::PerCircuitData *org_data = mp_cross_ref->per_circuit_data_for (circuits); @@ -575,6 +631,10 @@ size_t NetlistCrossReferenceModel::net_index (const net_pair &nets) const size_t NetlistCrossReferenceModel::device_index (const device_pair &devices) const { + if (! mp_cross_ref.get ()) { + return lay::no_netlist_index; + } + circuit_pair circuits = parent_of (devices); const db::NetlistCrossReference::PerCircuitData *org_data = mp_cross_ref->per_circuit_data_for (circuits); @@ -588,6 +648,10 @@ size_t NetlistCrossReferenceModel::device_index (const device_pair &devices) con size_t NetlistCrossReferenceModel::pin_index (const pin_pair &pins, const circuit_pair &circuits) const { + if (! mp_cross_ref.get ()) { + return lay::no_netlist_index; + } + const db::NetlistCrossReference::PerCircuitData *org_data = mp_cross_ref->per_circuit_data_for (circuits); if (! org_data) { return lay::no_netlist_index; @@ -599,6 +663,10 @@ size_t NetlistCrossReferenceModel::pin_index (const pin_pair &pins, const circui size_t NetlistCrossReferenceModel::subcircuit_index (const subcircuit_pair &subcircuits) const { + if (! mp_cross_ref.get ()) { + return lay::no_netlist_index; + } + circuit_pair circuits = parent_of (subcircuits); const db::NetlistCrossReference::PerCircuitData *org_data = mp_cross_ref->per_circuit_data_for (circuits); @@ -612,6 +680,10 @@ size_t NetlistCrossReferenceModel::subcircuit_index (const subcircuit_pair &subc std::string NetlistCrossReferenceModel::circuit_pair_status_hint (const std::pair > &cps) const { + if (! mp_cross_ref.get ()) { + return std::string (); + } + std::string msg; if (cps.second.first == db::NetlistCrossReference::Mismatch || cps.second.first == db::NetlistCrossReference::NoMatch) { @@ -655,6 +727,10 @@ std::string NetlistCrossReferenceModel::circuit_status_hint (size_t index) const std::string NetlistCrossReferenceModel::child_circuit_status_hint (const circuit_pair &circuits, size_t index) const { + if (! mp_cross_ref.get ()) { + return std::string (); + } + std::string msg; std::pair > cps = child_circuit_from_index (circuits, index); @@ -685,6 +761,10 @@ std::string NetlistCrossReferenceModel::child_circuit_status_hint (const circuit std::string NetlistCrossReferenceModel::net_status_hint (const circuit_pair &circuits, size_t index) const { + if (! mp_cross_ref.get ()) { + return std::string (); + } + std::string msg; std::pair > cps = net_from_index (circuits, index); @@ -713,6 +793,10 @@ std::string NetlistCrossReferenceModel::net_status_hint (const circuit_pair &cir std::string NetlistCrossReferenceModel::device_status_hint (const circuit_pair &circuits, size_t index) const { + if (! mp_cross_ref.get ()) { + return std::string (); + } + std::string msg; std::pair > cps = device_from_index (circuits, index); @@ -748,6 +832,10 @@ std::string NetlistCrossReferenceModel::device_status_hint (const circuit_pair & std::string NetlistCrossReferenceModel::pin_status_hint (const circuit_pair &circuits, size_t index) const { + if (! mp_cross_ref.get ()) { + return std::string (); + } + std::string msg; std::pair > cps = pin_from_index (circuits, index); @@ -772,6 +860,10 @@ std::string NetlistCrossReferenceModel::pin_status_hint (const circuit_pair &cir std::string NetlistCrossReferenceModel::subcircuit_status_hint (const circuit_pair &circuits, size_t index) const { + if (! mp_cross_ref.get ()) { + return std::string (); + } + std::string msg; std::pair > cps = subcircuit_from_index (circuits, index); diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index a5603bd3f..ac141d640 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -301,3 +301,38 @@ TEST(40_DeviceExtractorErrors) run_test (_this, "custom_resistors", "custom_resistors.gds", true, false /*no LVS*/); } +// Basic soft connection +TEST(50_BasicSoftConnection) +{ + run_test (_this, "soft_connect1", "soft_connect1.gds", true, false /*no LVS*/); +} + +// No errors +TEST(51_SoftConnectionNoErrors) +{ + run_test (_this, "soft_connect2", "soft_connect2.gds", true, false /*no LVS*/); +} + +// Simple hierarchy +TEST(52_SoftConnectionSimpleHierarchy) +{ + run_test (_this, "soft_connect3", "soft_connect3.gds", true, false /*no LVS*/); +} + +// Soft connected nets from different subcircuits +TEST(53_SoftConnectionFromSubcircuits) +{ + run_test (_this, "soft_connect4", "soft_connect4.gds", true, false /*no LVS*/); +} + +// Soft connected nets from different subcircuits (propagated) +TEST(54_SoftConnectionFromSubcircuits2) +{ + run_test (_this, "soft_connect5", "soft_connect5.gds", true, false /*no LVS*/); +} + +// Level 2 soft connection +TEST(55_SoftConnectionSecondLevel) +{ + run_test (_this, "soft_connect6", "soft_connect6.gds", true, false /*no LVS*/); +} diff --git a/src/lvs/unit_tests/lvsTests.cc b/src/lvs/unit_tests/lvsTests.cc index 616b561e2..a21008c38 100644 --- a/src/lvs/unit_tests/lvsTests.cc +++ b/src/lvs/unit_tests/lvsTests.cc @@ -97,6 +97,12 @@ TEST(2_fullWithAlign) run_test (_this, "vexriscv_align.lvs", "vexriscv.cir.gz", "vexriscv.oas.gz"); } +TEST(3_fullSoft) +{ + test_is_long_runner (); + run_test (_this, "vexriscv_soft.lvs", "vexriscv.cir.gz", "vexriscv.oas.gz"); +} + TEST(10_private) { // test_is_long_runner (); diff --git a/src/rba/unit_tests/rbaTests.cc b/src/rba/unit_tests/rbaTests.cc index 3aa050836..c34b71bda 100644 --- a/src/rba/unit_tests/rbaTests.cc +++ b/src/rba/unit_tests/rbaTests.cc @@ -102,6 +102,7 @@ RUBYTEST (dbEdgePairTest, "dbEdgePairTest.rb") RUBYTEST (dbEdgesTest, "dbEdgesTest.rb") RUBYTEST (dbEdgeTest, "dbEdgeTest.rb") RUBYTEST (dbGlyphs, "dbGlyphs.rb") +RUBYTEST (dbHierNetworkProcessorTests, "dbHierNetworkProcessorTests.rb") RUBYTEST (dbInstanceTest, "dbInstanceTest.rb") RUBYTEST (dbInstElementTest, "dbInstElementTest.rb") RUBYTEST (dbLayerMapping, "dbLayerMapping.rb") diff --git a/testdata/algo/soft_connections.gds b/testdata/algo/soft_connections.gds new file mode 100644 index 000000000..7a92fe328 Binary files /dev/null and b/testdata/algo/soft_connections.gds differ diff --git a/testdata/algo/soft_connections_au.gds b/testdata/algo/soft_connections_au.gds new file mode 100644 index 000000000..8a34b60f7 Binary files /dev/null and b/testdata/algo/soft_connections_au.gds differ diff --git a/testdata/lvs/double_height2.lvsdb b/testdata/lvs/double_height2.lvsdb index a1ff6a9d4..3c2330e64 100644 --- a/testdata/lvs/double_height2.lvsdb +++ b/testdata/lvs/double_height2.lvsdb @@ -29,7 +29,7 @@ J( G(l14 SUBSTRATE) H(W B('Must-connect nets GND must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) H(W B('Must-connect nets R must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) - H(W B('Must-connect nets R of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN:$1[r0 *1 0,0]/INV2') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) + H(W B('Must-connect nets R of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN/INV2[r0 0,0]:$1') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) K(PMOS MOS3) K(NMOS MOS3) D(D$PMOS PMOS diff --git a/testdata/lvs/double_height2_texts.lvsdb b/testdata/lvs/double_height2_texts.lvsdb index 049cec097..6dc2a6872 100644 --- a/testdata/lvs/double_height2_texts.lvsdb +++ b/testdata/lvs/double_height2_texts.lvsdb @@ -29,7 +29,7 @@ J( G(l14 SUBSTRATE) H(W B('Must-connect nets GND must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) H(W B('Must-connect nets R must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) - H(W B('Must-connect nets R of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN:$1[r0 *1 0,0]/INV2') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) + H(W B('Must-connect nets R of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN/INV2[r0 0,0]:$1') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) K(PMOS MOS3) K(NMOS MOS3) D(D$PMOS PMOS diff --git a/testdata/lvs/must_connect2.lvsdb b/testdata/lvs/must_connect2.lvsdb index 02ee5a88c..8a9daf6e2 100644 --- a/testdata/lvs/must_connect2.lvsdb +++ b/testdata/lvs/must_connect2.lvsdb @@ -29,7 +29,7 @@ J( G(l14 SUBSTRATE) H(W B('Must-connect nets VSSTOP must be connected further up in the hierarchy - this is an error at chip top level') C(TOP) X('must-connect')) H(W B('Must-connect nets VDD must be connected further up in the hierarchy - this is an error at chip top level') C(TOP) X('must-connect')) - H(W B('Must-connect nets VSS of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN:$2[r0 *1 0,0]/INV2') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) + H(W B('Must-connect nets VSS of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN/INV2[r0 0,0]:$2') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) K(PMOS MOS3) K(NMOS MOS3) D(D$PMOS PMOS diff --git a/testdata/lvs/soft_connect1.cir b/testdata/lvs/soft_connect1.cir new file mode 100644 index 000000000..61ac75956 --- /dev/null +++ b/testdata/lvs/soft_connect1.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_connect1.gds b/testdata/lvs/soft_connect1.gds new file mode 100644 index 000000000..f2ff6e943 Binary files /dev/null and b/testdata/lvs/soft_connect1.gds differ diff --git a/testdata/lvs/soft_connect1.l2n b/testdata/lvs/soft_connect1.l2n new file mode 100644 index 000000000..2e2c95ba8 --- /dev/null +++ b/testdata/lvs/soft_connect1.l2n @@ -0,0 +1,217 @@ +#%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) +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_connect1.lvs b/testdata/lvs/soft_connect1.lvs new file mode 100644 index 000000000..a4ed6551d --- /dev/null +++ b/testdata/lvs/soft_connect1.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 +connect_global(bulk, "SUBSTRATE") +soft_connect_global(ptie, "SUBSTRATE") + +# Netlist section (NOTE: we only check log here) +netlist + +netlist.simplify + + diff --git a/testdata/lvs/soft_connect2.cir b/testdata/lvs/soft_connect2.cir new file mode 100644 index 000000000..61ac75956 --- /dev/null +++ b/testdata/lvs/soft_connect2.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_connect2.gds b/testdata/lvs/soft_connect2.gds new file mode 100644 index 000000000..a223df098 Binary files /dev/null and b/testdata/lvs/soft_connect2.gds differ diff --git a/testdata/lvs/soft_connect2.l2n b/testdata/lvs/soft_connect2.l2n new file mode 100644 index 000000000..d75ed206d --- /dev/null +++ b/testdata/lvs/soft_connect2.l2n @@ -0,0 +1,213 @@ +#%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) +G(l9 SUBSTRATE) +GS(l9 SUBSTRATE) +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 (-4400 -350) (1200 900)) + R(l14 (-4800 -900) (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 (-4400 -350) (1200 900)) + R(l14 (-4800 -900) (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_connect2.lvs b/testdata/lvs/soft_connect2.lvs new file mode 100644 index 000000000..a4ed6551d --- /dev/null +++ b/testdata/lvs/soft_connect2.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 +connect_global(bulk, "SUBSTRATE") +soft_connect_global(ptie, "SUBSTRATE") + +# Netlist section (NOTE: we only check log here) +netlist + +netlist.simplify + + diff --git a/testdata/lvs/soft_connect3.cir b/testdata/lvs/soft_connect3.cir new file mode 100644 index 000000000..e342448fa --- /dev/null +++ b/testdata/lvs/soft_connect3.cir @@ -0,0 +1,21 @@ +* 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 +X$1 \$5 \$1 \$4 SUBSTRATE NTRANS +X$2 \$5 \$2 \$4 \$3 PTRANS +.ENDS INV + +.SUBCKT PTRANS \$1 \$3 \$5 \$I3 +M$1 \$3 \$5 \$1 \$I3 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +.ENDS PTRANS + +.SUBCKT NTRANS \$1 \$3 \$5 SUBSTRATE +M$1 \$3 \$5 \$1 SUBSTRATE NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +.ENDS NTRANS diff --git a/testdata/lvs/soft_connect3.gds b/testdata/lvs/soft_connect3.gds new file mode 100644 index 000000000..62a10be1e Binary files /dev/null and b/testdata/lvs/soft_connect3.gds differ diff --git a/testdata/lvs/soft_connect3.l2n b/testdata/lvs/soft_connect3.l2n new file mode 100644 index 000000000..f046069b3 --- /dev/null +++ b/testdata/lvs/soft_connect3.l2n @@ -0,0 +1,257 @@ +#%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) +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(NTRANS + R((-1000 -800) (2000 1600)) + N(1 + R(l8 (-510 -310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (-290 -690) (360 760)) + R(l6 (-680 -855) (775 950)) + ) + N(2 + R(l8 (290 -310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (-290 -690) (360 760)) + R(l6 (-455 -855) (775 950)) + ) + N(3 + R(l4 (-125 -800) (250 1600)) + ) + N(4 I(SUBSTRATE)) + P(1) + P(2) + P(3) + P(4 I(SUBSTRATE)) + D(1 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 1) + T(G 3) + T(D 2) + T(B 4) + ) +) +X(PTRANS + R((-1000 -800) (2000 1600)) + N(1 + R(l8 (-510 -310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (-290 -690) (360 760)) + R(l2 (-680 -855) (775 950)) + ) + N(2 + R(l8 (290 -310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (-290 -690) (360 760)) + R(l2 (-455 -855) (775 950)) + ) + N(3 + R(l4 (-125 -800) (250 1600)) + ) + N(4) + P(1) + P(2) + P(3) + P(4) + D(1 D$PMOS + 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 1) + T(G 3) + T(D 2) + T(B 4) + ) +) +X(INV + R((-1500 -800) (3000 4600)) + N(1 + R(l13 (275 -325) (250 250)) + R(l13 (-250 150) (250 250)) + R(l14 (-2025 -775) (3000 900)) + ) + N(2 + R(l13 (275 2475) (250 250)) + R(l13 (-250 150) (250 250)) + R(l14 (-2025 -775) (3000 900)) + ) + N(3 + R(l3 (-1500 1800) (3000 2000)) + ) + N(4 + R(l4 (-125 -250) (250 2500)) + ) + N(5 + R(l12 (-580 -420) (360 2840)) + ) + N(6 I(SUBSTRATE)) + P(1) + P(2) + P(3) + P(4) + P(5) + P(6 I(SUBSTRATE)) + X(1 NTRANS Y(0 0) + P(0 5) + P(1 1) + P(2 4) + P(3 6) + ) + X(2 PTRANS Y(0 2800) + P(0 5) + P(1 2) + P(2 4) + P(3 3) + ) +) +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_connect3.lvs b/testdata/lvs/soft_connect3.lvs new file mode 100644 index 000000000..a4ed6551d --- /dev/null +++ b/testdata/lvs/soft_connect3.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 +connect_global(bulk, "SUBSTRATE") +soft_connect_global(ptie, "SUBSTRATE") + +# Netlist section (NOTE: we only check log here) +netlist + +netlist.simplify + + diff --git a/testdata/lvs/soft_connect4.cir b/testdata/lvs/soft_connect4.cir new file mode 100644 index 000000000..091f20176 --- /dev/null +++ b/testdata/lvs/soft_connect4.cir @@ -0,0 +1,13 @@ +* Extracted by KLayout + +.SUBCKT TOP A Q SUBSTRATE +X$1 \$5 \$1 Q SUBSTRATE INV +X$2 \$5 A \$1 SUBSTRATE INV +.ENDS TOP + +.SUBCKT INV \$2 \$4 \$5 SUBSTRATE +M$1 \$2 \$4 \$5 \$2 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +M$2 SUBSTRATE \$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_connect4.gds b/testdata/lvs/soft_connect4.gds new file mode 100644 index 000000000..5c19d896d Binary files /dev/null and b/testdata/lvs/soft_connect4.gds differ diff --git a/testdata/lvs/soft_connect4.l2n b/testdata/lvs/soft_connect4.l2n new file mode 100644 index 000000000..402240d29 --- /dev/null +++ b/testdata/lvs/soft_connect4.l2n @@ -0,0 +1,192 @@ +#%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) +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) +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/INV[r0 3,1.6]:$1 - $2') C(TOP) Q('(1.5,3.95;1.5,4.85;5.3,4.85;5.3,3.95)')) +H(B('\tPartial net #2: TOP/INV[r0 7.7,1.6]:$2 - $2') C(TOP) Q('(6.2,3.95;6.2,4.85;10,4.85;10,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/INV[r0 3,1.6]:$1 - $1') C(TOP) Q('(1.5,1.15;1.5,2.05;5.3,2.05;5.3,1.15)')) +H(B('\tPartial net #2: TOP/INV[r0 7.7,1.6]:$2 - $1') C(TOP) Q('(6.2,1.15;6.2,2.05;10,2.05;10,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) (3800 4600)) + N(1 I(SUBSTRATE) + R(l8 (1700 100) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l8 (-1610 -210) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (890 -760) (800 900)) + R(l12 (-1980 -830) (360 760)) + R(l13 (-305 -705) (250 250)) + R(l13 (-250 150) (250 250)) + R(l13 (1175 -225) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-3400 -350) (3000 900)) + R(l14 (-200 -900) (1000 900)) + R(l6 (-2175 -925) (775 950)) + ) + N(2 + R(l3 (-1500 1800) (3000 2000)) + R(l3 (-200 -2000) (1000 2000)) + R(l8 (-2010 -1310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l8 (1190 -210) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l12 (-1680 -280) (360 760)) + R(l12 (820 -830) (800 900)) + R(l13 (-1925 -775) (250 250)) + R(l13 (-250 150) (250 250)) + R(l13 (1175 -225) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-3400 -350) (3000 900)) + R(l14 (-200 -900) (1000 900)) + R(l10 (-700 -950) (400 1000)) + R(l2 (-1875 -975) (775 950)) + ) + N(3 + R(l4 (-125 -250) (250 2500)) + R(l4 (-250 -3050) (250 1600)) + R(l4 (-250 1200) (250 1600)) + ) + N(4 + 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)) + ) + P(2) + P(3) + P(4) + P(1 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 4) + T(G 3) + T(D 2) + T(B 2) + ) + 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 4) + T(G 3) + T(D 1) + T(B 1) + ) +) +X(TOP + R((1500 800) (9080 4600)) + N(1 + R(l4 (2920 2600) (3980 400)) + R(l11 (-300 -300) (200 200)) + R(l12 (-300 -300) (690 400)) + ) + N(2 I(A) + R(l4 (7700 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 + R(l3 (4000 3400) (2700 2000)) + ) + N(5 I(SUBSTRATE)) + P(2 I(A)) + P(3 I(Q)) + P(5 I(SUBSTRATE)) + X(1 INV Y(3000 1600) + P(0 4) + P(1 1) + P(2 3) + P(3 5) + ) + X(2 INV Y(7700 1600) + P(0 4) + P(1 2) + P(2 1) + P(3 5) + ) +) diff --git a/testdata/lvs/soft_connect4.lvs b/testdata/lvs/soft_connect4.lvs new file mode 100644 index 000000000..fcf0d3d22 --- /dev/null +++ b/testdata/lvs/soft_connect4.lvs @@ -0,0 +1,93 @@ + +$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 +connect_global(bulk, "SUBSTRATE") +soft_connect_global(ptie, "SUBSTRATE") + +# Netlist section (NOTE: we only check log here) +# for debugging: _make_soft_connection_diodes(true) +netlist + +netlist.simplify + + diff --git a/testdata/lvs/soft_connect5.cir b/testdata/lvs/soft_connect5.cir new file mode 100644 index 000000000..9921ebb1b --- /dev/null +++ b/testdata/lvs/soft_connect5.cir @@ -0,0 +1,13 @@ +* Extracted by KLayout + +.SUBCKT TOP A Q VDD SUBSTRATE|VSS +X$1 SUBSTRATE|VSS VDD \$1 Q INV +X$2 SUBSTRATE|VSS VDD A \$1 INV +.ENDS TOP + +.SUBCKT INV SUBSTRATE \$2 \$4 \$5 +M$1 \$2 \$4 \$5 \$2 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +M$2 SUBSTRATE \$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_connect5.gds b/testdata/lvs/soft_connect5.gds new file mode 100644 index 000000000..a33882ebc Binary files /dev/null and b/testdata/lvs/soft_connect5.gds differ diff --git a/testdata/lvs/soft_connect5.l2n b/testdata/lvs/soft_connect5.l2n new file mode 100644 index 000000000..d2a3334b8 --- /dev/null +++ b/testdata/lvs/soft_connect5.l2n @@ -0,0 +1,196 @@ +#%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) +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('(1.5,3.95;1.5,4.85;5.3,4.85;5.3,3.95)')) +H(B('\tPartial net #2: TOP - $I7') C(TOP) Q('(6.2,3.95;6.2,4.85;10,4.85;10,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('(1.5,1.15;1.5,2.05;5.3,2.05;5.3,1.15)')) +H(B('\tPartial net #2: TOP - $I6') C(TOP) Q('(6.2,1.15;6.2,2.05;10,2.05;10,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) (3800 4600)) + N(1 I(SUBSTRATE) + R(l8 (1700 100) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l8 (-1610 -210) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (890 -760) (800 900)) + R(l12 (-1980 -830) (360 760)) + R(l13 (-305 -705) (250 250)) + R(l13 (-250 150) (250 250)) + R(l13 (1175 -225) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-3400 -350) (3000 900)) + R(l14 (-200 -900) (1000 900)) + R(l6 (-2175 -925) (775 950)) + ) + N(2 + R(l3 (-1500 1800) (3000 2000)) + R(l3 (-200 -2000) (1000 2000)) + R(l8 (-2010 -1310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l8 (1190 -210) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l12 (-1680 -280) (360 760)) + R(l12 (820 -830) (800 900)) + R(l13 (-1925 -775) (250 250)) + R(l13 (-250 150) (250 250)) + R(l13 (1175 -225) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-3400 -350) (3000 900)) + R(l14 (-200 -900) (1000 900)) + R(l10 (-700 -950) (400 1000)) + R(l2 (-1875 -975) (775 950)) + ) + N(3 + R(l4 (-125 -250) (250 2500)) + R(l4 (-250 -3050) (250 1600)) + R(l4 (-250 1200) (250 1600)) + ) + N(4 + 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)) + ) + P(1 I(SUBSTRATE)) + P(2) + P(3) + P(4) + 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 4) + T(G 3) + T(D 2) + T(B 2) + ) + 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 4) + T(G 3) + T(D 1) + T(B 1) + ) +) +X(TOP + R((1500 800) (9080 4600)) + N(1 + R(l4 (2920 2600) (3980 400)) + R(l11 (-300 -300) (200 200)) + R(l12 (-300 -300) (690 400)) + ) + N(2 I(A) + R(l4 (7700 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) (2700 2000)) + R(l17 (-2500 -1000) (0 0)) + ) + N(5 I('SUBSTRATE,VSS') + R(l17 (4200 1500) (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 1) + P(3 3) + ) + X(2 INV Y(7700 1600) + P(0 5) + P(1 4) + P(2 2) + P(3 1) + ) +) diff --git a/testdata/lvs/soft_connect5.lvs b/testdata/lvs/soft_connect5.lvs new file mode 100644 index 000000000..fcf0d3d22 --- /dev/null +++ b/testdata/lvs/soft_connect5.lvs @@ -0,0 +1,93 @@ + +$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 +connect_global(bulk, "SUBSTRATE") +soft_connect_global(ptie, "SUBSTRATE") + +# Netlist section (NOTE: we only check log here) +# for debugging: _make_soft_connection_diodes(true) +netlist + +netlist.simplify + + diff --git a/testdata/lvs/soft_connect6.cir b/testdata/lvs/soft_connect6.cir new file mode 100644 index 000000000..903b48dc9 --- /dev/null +++ b/testdata/lvs/soft_connect6.cir @@ -0,0 +1,13 @@ +* Extracted by KLayout + +.SUBCKT TOP A Q VDD SUBSTRATE|VSS +X$1 Q SUBSTRATE|VSS VDD \$1 INV +X$2 \$1 SUBSTRATE|VSS VDD A INV +.ENDS TOP + +.SUBCKT INV \$1 SUBSTRATE \$4 \$6 +M$1 \$4 \$6 \$1 \$4 PMOS L=0.25U W=0.95U AS=1.02125P AD=0.73625P PS=4.05U ++ PD=3.45U +M$2 SUBSTRATE \$6 \$1 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_connect6.gds b/testdata/lvs/soft_connect6.gds new file mode 100644 index 000000000..30da12151 Binary files /dev/null and b/testdata/lvs/soft_connect6.gds differ diff --git a/testdata/lvs/soft_connect6.l2n b/testdata/lvs/soft_connect6.l2n new file mode 100644 index 000000000..600f9b0bc --- /dev/null +++ b/testdata/lvs/soft_connect6.l2n @@ -0,0 +1,201 @@ +#%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) +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 - $2') C(TOP) Q('(6.54,2.6;6.54,4.78;6.9,4.78;6.9,2.6)')) +H(B('\tPartial net #2: TOP - $I3') C(TOP) Q('(7.12,1.18;7.12,4.78;7.48,4.78;7.48,1.18)')) +H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check')) +H(B('\tPartial net #1: TOP - Q') C(TOP) Q('(1.81,1.18;1.81,4.78;2.78,4.78;2.78,1.18)')) +H(B('\tPartial net #2: TOP - $I2') C(TOP) Q('(1.84,4.02;1.84,4.78;2.2,4.78;2.2,4.02)')) +K(PMOS MOS4) +K(NMOS MOS4) +D(D$PMOS PMOS + T(S + R(l2 (-1200 -475) (1075 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) (3800 4600)) + N(1 + R(l8 (-1090 2490) (220 220)) + R(l8 (-220 180) (220 220)) + R(l8 (360 -3420) (220 220)) + R(l8 (-220 180) (220 220)) + R(l8 (-220 2180) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (-870 -690) (360 760)) + R(l12 (220 -3600) (360 2840)) + R(l12 (-360 -2800) (360 760)) + R(l12 (-360 2040) (360 760)) + R(l2 (-980 -855) (1075 950)) + R(l6 (-775 -3750) (775 950)) + ) + N(2 I(SUBSTRATE) + R(l8 (1700 100) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l8 (-1610 -210) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (890 -760) (800 900)) + R(l12 (-1980 -830) (360 760)) + R(l13 (-305 -705) (250 250)) + R(l13 (-250 150) (250 250)) + R(l13 (1175 -225) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-3400 -350) (3000 900)) + R(l14 (-200 -900) (1000 900)) + R(l6 (-2175 -925) (775 950)) + ) + N(3 + R(l3 (-1500 1800) (3000 2000)) + R(l3 (-200 -2000) (1000 2000)) + R(l8 (-2010 -1310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l8 (1190 -210) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l12 (-1680 -280) (360 760)) + R(l12 (820 -830) (800 900)) + R(l13 (-1925 -775) (250 250)) + R(l13 (-250 150) (250 250)) + R(l13 (1175 -225) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-3400 -350) (3000 900)) + R(l14 (-200 -900) (1000 900)) + R(l10 (-700 -950) (400 1000)) + R(l2 (-1875 -975) (775 950)) + ) + N(4 + R(l4 (-125 -250) (250 2500)) + R(l4 (-250 -3050) (250 1600)) + R(l4 (-250 1200) (250 1600)) + ) + P(1) + P(2 I(SUBSTRATE)) + P(3) + P(4) + D(1 D$PMOS + Y(0 2800) + E(L 0.25) + E(W 0.95) + E(AS 1.02125) + E(AD 0.73625) + E(PS 4.05) + E(PD 3.45) + T(S 1) + T(G 4) + T(D 3) + 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 1) + T(G 4) + T(D 2) + T(B 2) + ) +) +X(TOP + R((1500 800) (9080 4600)) + N(1 + R(l4 (2920 2600) (3980 400)) + R(l11 (-300 -300) (200 200)) + R(l12 (-260 -300) (360 1600)) + ) + N(2 I(A) + R(l4 (7700 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) (2700 2000)) + R(l14 (-1500 -1450) (1200 900)) + R(l17 (-2200 -450) (0 0)) + ) + N(5 I('SUBSTRATE,VSS') + R(l14 (5200 1150) (1200 900)) + R(l17 (-2200 -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 3) + P(1 5) + P(2 4) + P(3 1) + ) + X(2 INV Y(7700 1600) + P(0 1) + P(1 5) + P(2 4) + P(3 2) + ) +) diff --git a/testdata/lvs/soft_connect6.lvs b/testdata/lvs/soft_connect6.lvs new file mode 100644 index 000000000..fcf0d3d22 --- /dev/null +++ b/testdata/lvs/soft_connect6.lvs @@ -0,0 +1,93 @@ + +$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 +connect_global(bulk, "SUBSTRATE") +soft_connect_global(ptie, "SUBSTRATE") + +# Netlist section (NOTE: we only check log here) +# for debugging: _make_soft_connection_diodes(true) +netlist + +netlist.simplify + + diff --git a/testdata/lvs/vexriscv_soft.lvs b/testdata/lvs/vexriscv_soft.lvs new file mode 100644 index 000000000..5dc34db69 --- /dev/null +++ b/testdata/lvs/vexriscv_soft.lvs @@ -0,0 +1,111 @@ + +source($lvs_test_source) + +# will get pretty big: +# report_lvs($lvs_test_target_lvsdb, true) + +target_netlist($lvs_test_target_cir, write_spice(true), "Extracted by KLayout") + +schematic("vexriscv_schematic.cir.gz") + +deep + +# Drawing layers + +nwell = input(1, 0) +pactive = input(4, 0) +nactive = input(3, 0) +ntie = input(5, 0) +ptie = input(6, 0) + +poly = input(7, 0) +cont = input(10, 0) +metal1 = input(11, 0) +via1 = input(14, 0) +metal2 = input(16, 0) +via2 = input(18, 0) +metal3 = input(19, 0) +via3 = input(21, 0) +metal4 = input(22, 0) +via4 = input(25, 0) +metal5 = input(26, 0) + +# Bulk layer for terminal provisioning + +bulk = polygon_layer + +# Computed layers + +poly_cont = cont & poly +diff_cont = cont - poly + +pgate = pactive & poly +psd = pactive - pgate + +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, "tW" => nwell }) + +# NMOS transistor device extraction +extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk, + "tS" => nsd, "tD" => nsd, "tG" => poly, "tW" => bulk }) + +# Define connectivity for netlist extraction + +# Inter-layer +soft_connect(diff_cont, psd) +soft_connect(diff_cont, nsd) +soft_connect(poly_cont, poly) +connect(poly_cont, metal1) +connect(diff_cont, metal1) +soft_connect(diff_cont, ntie) +soft_connect(diff_cont, ptie) +soft_connect(ntie, nwell) +connect(metal1, via1) +connect(via1, metal2) +connect(metal2, via2) +connect(via2, metal3) +connect(metal3, via3) +connect(via3, metal4) +connect(metal4, via4) +connect(via4, metal5) + +# Global +soft_connect_global(ptie, "BULK") +connect_global(bulk, "BULK") + +# Implicit +connect_implicit("VDD") +connect_implicit("VSS") + +# Compare section + +same_device_classes("PMOS", "TP") +same_device_classes("NMOS", "TN") + +# Ignore all caps from the schematic +same_device_classes(nil, "CAP") + +# Increase the default complexity from 100 to 200 +# This is required because the clock tree is incorrect and exhibits manifold ambiguities +# (the netlists are just samples, not necessarily functional). +# The algorithm needs enough freedom to follow all these branches and different variants. +max_branch_complexity(200) + +schematic.combine_devices + +netlist.combine_devices + +align + +if ! compare + raise "Netlists don't match" +else + puts "Congratulations! Netlists match." +end + diff --git a/testdata/ruby/dbHierNetworkProcessorTests.rb b/testdata/ruby/dbHierNetworkProcessorTests.rb new file mode 100644 index 000000000..bd824e88e --- /dev/null +++ b/testdata/ruby/dbHierNetworkProcessorTests.rb @@ -0,0 +1,57 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2024 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBHierNetworkProcessor_TestClass < TestBase + + # Connectivity + def test_1_Connectivity + + conn = RBA::Connectivity::new + + assert_equal(conn.to_s, "") + + conn.connect(1) + assert_equal(conn.to_s, "1:1") + + conn.connect(0, 1) + assert_equal(conn.to_s, "0:1\n1:0,1") + + conn.soft_connect(0, 2) + assert_equal(conn.to_s, "0:1,2-S\n1:0,1\n2:0+S") + + gid1 = conn.connect_global(0, "GLOBAL1") + assert_equal(gid1, 0) + assert_equal(conn.global_net_name(gid1), "GLOBAL1") + assert_equal(conn.global_net_id("GLOBAL1"), 0) + assert_equal(conn.to_s, "0:1,2-S\nG0:0\n1:0,1\n2:0+S") + + conn.soft_connect_global(1, "GLOBAL1") + assert_equal(conn.to_s, "0:1,2-S\nG0:0\n1:0,1\nG1:0-S\n2:0+S") + + end + +end + +load("test_epilogue.rb")