From 284a907ffb02ef2c590f27f428e85570c940a743 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 10 Mar 2024 23:35:56 +0100 Subject: [PATCH 01/42] First steps: equipping the Connectivity object with soft connections --- src/db/db/dbHierNetworkProcessor.cc | 98 +++++++++++++------ src/db/db/dbHierNetworkProcessor.h | 66 +++++++++++-- src/db/db/dbLayoutToNetlist.cc | 58 ++++++++++- src/db/db/dbLayoutToNetlist.h | 55 +++++++++++ src/db/db/dbLayoutToNetlistFormatDefs.cc | 4 + src/db/db/dbLayoutToNetlistFormatDefs.h | 11 ++- src/db/db/dbLayoutToNetlistReader.cc | 34 ++++++- src/db/db/dbLayoutToNetlistWriter.cc | 60 +++++++++--- .../unit_tests/dbHierNetworkProcessorTests.cc | 88 +++++++++++------ src/layui/layui/layNetInfoDialog.cc | 4 +- src/layui/layui/layNetlistBrowserDialog.cc | 2 +- src/layui/layui/layNetlistBrowserPage.cc | 4 +- 12 files changed, 390 insertions(+), 94 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 066d8eeb0..8ac6a9581 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; // ------------------------------------------------------------------------------ @@ -496,7 +532,8 @@ public: void add (const T *s1, unsigned int l1, const T *s2, unsigned int l2) { - if (mp_conn->interacts (*s1, l1, *s2, l2, m_trans)) { + int soft; // @@@ + if (mp_conn->interacts (*s1, l1, *s2, l2, m_trans, soft)) { m_any = true; } } @@ -559,7 +596,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); // @@@ soft connections? } if (! box.empty () && ! s->second.begin_touching (box.transformed (trans), bc).at_end ()) { @@ -933,7 +970,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; // @@@ + if (! mp_conn->interacts (*s1, p1.first, *s2, p2.first, soft)) { return; } @@ -996,12 +1034,12 @@ 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); // @@@ soft connections if (icg == m_global_to_clusters.end ()) { - ic->second->second.insert (*g); - m_global_to_clusters.insert (std::make_pair (*g, ic->second)); + ic->second->second.insert (g->first); // @@@ soft connections? + m_global_to_clusters.insert (std::make_pair (g->first, ic->second)); // @@@ soft connections? } else if (ic->second != icg->second) { @@ -1110,7 +1148,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))); @@ -1643,13 +1681,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); // @@@ soft connections? box_type bb2 = i2.cell_inst ().bbox (bcb).transformed (t2); any = bb1.touches (bb2); } @@ -2186,6 +2224,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 +2238,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; diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 13c557d64..72bb54302 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; diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 62d2dc287..117c831af 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -298,6 +298,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 +333,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 +395,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; +} +// @@@ + void LayoutToNetlist::extract_netlist () { if (m_netlist_extracted) { @@ -371,7 +424,10 @@ 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); + // @@@ NOTE: can we have multiple pins on a net? Will this happen later maybe? + // @@@ do_soft_connections () do_join_nets (); + tl_assert (check_many_pins (mp_netlist.get ())); // @@@ if (tl::verbosity () >= 41) { MemStatisticsCollector m (false); @@ -870,7 +926,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); } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 493450194..4996f6cbf 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 */ @@ -1035,6 +1088,8 @@ 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 (); 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/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/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 7ad05b9a3..d4024b97b 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"); @@ -121,18 +143,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 +177,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) @@ -286,7 +310,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 += ";"; @@ -920,7 +944,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/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..66cbdec49 100644 --- a/src/layui/layui/layNetlistBrowserPage.cc +++ b/src/layui/layui/layNetlistBrowserPage.cc @@ -1421,7 +1421,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 +1580,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); From fd8ca56cafac8f21ad73ae398d4209de4e149ee1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 11 Mar 2024 21:14:00 +0100 Subject: [PATCH 02/42] Some tests for Connectivity --- src/db/db/gsiDeclDbHierNetworkProcessor.cc | 88 +++++++++++++++++++ .../unit_tests/dbHierNetworkProcessorTests.cc | 54 ++++++++++++ src/rba/unit_tests/rbaTests.cc | 1 + testdata/ruby/dbHierNetworkProcessorTests.rb | 57 ++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 testdata/ruby/dbHierNetworkProcessorTests.rb 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/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index d4024b97b..4034b099a 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -126,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; 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/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") From 98b66db832773152a2b4a64f870a8cd44ed00503 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 11 Mar 2024 22:37:01 +0100 Subject: [PATCH 03/42] Complete LVS sample --- samples/lvs/RINGO.cir | 27 ++++++++ samples/lvs/ringo.gds | Bin 9954 -> 10050 bytes samples/lvs/schematic.cir | 27 -------- samples/lvs/si4all.lvs | 128 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+), 27 deletions(-) create mode 100644 samples/lvs/RINGO.cir delete mode 100644 samples/lvs/schematic.cir create mode 100644 samples/lvs/si4all.lvs 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 fa116f14dc2079afc0090b1f54566677f25024e3..3d79ce740a11be70ff71dbb038fd820ea8283281 100644 GIT binary patch delta 484 zcmaFld&n<}fsKKQDS|{L4=vr&au+$Q;c@?37d&CSxEm?noa@lI}%DxPdA?J)VPl*8n;3bKm(2E4)1z>&d; U&&(HOm>Iz?h&!ZQ6z8!50HkF}_y7O^ delta 377 zcmX@)_sBPjfsKKQDS|{L4=vr&auHMc<{AY-+@>BA%ixq_XTa+$ zH+~~_LTV;($}>*RW#pZ_hqrigE;IM!Dn8T6`g{(H*K;vX{x2#!`3zq&P8UY7E8_Ip J<|T^rSOGMTKBNEu 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..2a6d82174 --- /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 + From 59a572344c1102542125bdd373f158380c4e9c47 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 11 Mar 2024 22:42:33 +0100 Subject: [PATCH 04/42] [consider merging] Bugfix: connect_explicit did not accept an array of nets as single argument --- src/drc/drc/built-in-macros/_drc_netter.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 1985cc67c..8c970483c 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -379,7 +379,8 @@ module DRC arg1.is_a?(String) || raise("The first argument has to be a string") @pre_extract_config << lambda { |l2n| l2n.join_nets(arg1, arg2) } else - arg1.is_a?(String) || raise("The argument has to be a string") + arg1.is_a?(Array) || raise("The argument has to be an array of strings") + arg1.find { |a| !a.is_a?(String) } && raise("The argument has to be an array of strings") @pre_extract_config << lambda { |l2n| l2n.join_nets(arg1) } end From bca58100870aae7a769cd1db102ce9f658638c35 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 11 Mar 2024 22:42:43 +0100 Subject: [PATCH 05/42] Updated LVS sample --- samples/lvs/si4all.lvs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/lvs/si4all.lvs b/samples/lvs/si4all.lvs index 2a6d82174..992369916 100644 --- a/samples/lvs/si4all.lvs +++ b/samples/lvs/si4all.lvs @@ -103,8 +103,8 @@ 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"]) +connect_explicit(["NWELL", "VDD"]) +connect_explicit(["BULK", "VSS"]) # Global connections connect_global(ptie, "BULK") From 89c281f87a0e8f882b6ce42a9767c4b84c825275 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 12 Mar 2024 23:24:54 +0100 Subject: [PATCH 06/42] Integration API into LVS+DRC --- samples/lvs/ringo.gds | Bin 10050 -> 10046 bytes src/db/db/gsiDeclDbLayoutToNetlist.cc | 44 ++++++++++++- src/drc/drc/built-in-macros/_drc_engine.rb | 26 ++++++++ src/drc/drc/built-in-macros/_drc_netter.rb | 72 +++++++++++++++++++++ 4 files changed, 141 insertions(+), 1 deletion(-) diff --git a/samples/lvs/ringo.gds b/samples/lvs/ringo.gds index 3d79ce740a11be70ff71dbb038fd820ea8283281..c29e3d5a61b53c7d4a6863b6ba2f472997bd8af9 100644 GIT binary patch delta 346 zcmX@)x6eWhbBEOULTw2zFJhp4_}daULrGfX6f{ delta 361 zcmdnzcgRnPfsKKQDS|rQ?q`FiquVZP1xr9UxE4rk(>d{3%)@!pRzi02JGpeMhMNclv+Ht6viXI(`uH#~um}P%I|G9ZD^3q@Zs6$TBvDHQyC` 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." ) + diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 14eb0c842..68a64d524 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -2244,12 +2244,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 @@ -2320,6 +2344,8 @@ CODE clear_connections connect connect_global + soft_connect + soft_connect_global connect_implicit connect_explicit device_scaling diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 8c970483c..0f977103d 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 From 8de70c742a6ab5b89b4fb8badc6ffa3bbc88a46a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 13 Mar 2024 00:25:19 +0100 Subject: [PATCH 07/42] WIP --- src/db/db/dbHierNetworkProcessor.cc | 171 ++++++++++++++---- src/db/db/dbHierNetworkProcessor.h | 2 +- src/db/db/dbLayoutToNetlist.cc | 3 +- .../unit_tests/dbHierNetworkProcessorTests.cc | 40 ++-- 4 files changed, 163 insertions(+), 53 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 8ac6a9581..2fe29c4ec 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -525,16 +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) { - int soft; // @@@ + int soft = 0; if (mp_conn->interacts (*s1, l1, *s2, l2, m_trans, soft)) { - m_any = true; + if (soft == 0 || (m_soft_mode != 0 && m_soft_mode != soft)) { + m_soft_mode = 0; + m_any = true; + } else { + m_soft_mode = soft; + } } } @@ -543,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; }; @@ -596,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->first); // @@@ soft connections? + box += cell.bbox (l->first); } if (! box.empty () && ! s->second.begin_touching (box.transformed (trans), bc).at_end ()) { @@ -610,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; @@ -669,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 @@ -970,7 +987,7 @@ struct cluster_building_receiver if (m_separate_attributes && p1.second != p2.second) { return; } - int soft; // @@@ + int soft = 0; if (! mp_conn->interacts (*s1, p1.first, *s2, p2.first, soft)) { return; } @@ -982,34 +999,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); + } + } } @@ -1034,20 +1105,42 @@ 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->first); // @@@ soft connections + 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->first); // @@@ soft connections? - m_global_to_clusters.insert (std::make_pair (g->first, ic->second)); // @@@ soft connections? + 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 (); + + 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); + } + } } @@ -1078,6 +1171,13 @@ private: m_clusters.erase (ic2); } + + void register_soft_connection (typename std::list::iterator ic1, typename std::list::iterator ic2, int soft) + { + + // @@@ TODO: implement + + } }; template @@ -1687,7 +1787,7 @@ private: 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->first); // @@@ soft connections? + db::box_convert bcb (*mp_layout, lb->first); box_type bb2 = i2.cell_inst ().bbox (bcb).transformed (t2); any = bb1.touches (bb2); } @@ -1860,7 +1960,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)) { + int soft = 0; + if ((! m_separate_attributes || i->same_attrs (*j)) && i->interacts (*j, t21, *mp_conn, soft)) { + // @@@ soft mode? new_interactions.push_back (std::make_pair (i->id (), j->id ())); } @@ -2032,8 +2134,13 @@ 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)) { + if (soft != 0) { + // @@@ TODO: soft mode + } else { + interactions.push_back (std::make_pair (c1.id (), j->id ())); + } } } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 72bb54302..e0c51fd53 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -327,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 diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 4bf968152..4bf49e1d3 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -1218,7 +1218,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/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 4034b099a..6c771ec19 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -293,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) @@ -326,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) From 6db937d9dfbe4d3d55417db62ddcdc3786b10297 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 14 Mar 2024 00:22:46 +0100 Subject: [PATCH 08/42] WIP --- src/db/db/dbHierNetworkProcessor.cc | 277 +++++++++++++++++++++------- src/db/db/dbHierNetworkProcessor.h | 63 ++++++- 2 files changed, 272 insertions(+), 68 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 2fe29c4ec..8377e3c52 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -879,6 +879,13 @@ 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) +{ + // @@@ TODO: implement +} + template void local_clusters::ensure_sorted () @@ -942,6 +949,8 @@ struct cluster_building_receiver void generate_clusters (local_clusters &clusters) { + std::map *> in_to_out; + // build the resulting clusters for (typename std::list::const_iterator c = m_clusters.begin (); c != m_clusters.end (); ++c) { @@ -952,6 +961,8 @@ struct cluster_building_receiver cluster->add_attr (s->second.second); } + in_to_out.insert (std::make_pair (c.operator-> (), cluster)); + std::set global_nets = c->second; // Add the global nets we derive from the attribute equivalence (join_nets of labelled vs. @@ -980,6 +991,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->id (), c2->second->id ()); + + } } void add (const T *s1, std::pair p1, const T *s2, std::pair p2) @@ -1153,6 +1178,7 @@ 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; @@ -1170,13 +1196,35 @@ private: } m_clusters.erase (ic2); + + auto i1 = m_soft_connections.find (std::make_pair (ic1.operator-> (), ic2.operator-> ())); + if (i1 != m_soft_connections.end ()) { + i1->second = 0; + } + + auto i2 = m_soft_connections.find (std::make_pair (ic2.operator-> (), ic1.operator-> ())); + if (i2 != m_soft_connections.end ()) { + i2->second = 0; + } } void register_soft_connection (typename std::list::iterator ic1, typename std::list::iterator ic2, int soft) { + auto i1 = m_soft_connections.find (std::make_pair (ic1.operator-> (), ic2.operator-> ())); + auto i2 = m_soft_connections.find (std::make_pair (ic2.operator-> (), ic1.operator-> ())); - // @@@ TODO: implement + 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 (ic1.operator-> (), ic2.operator-> ()), soft)); + m_soft_connections.insert (std::make_pair (std::make_pair (ic2.operator-> (), ic1.operator-> ()), -soft)); + } } }; @@ -1269,6 +1317,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; @@ -1533,17 +1582,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; }; /** @@ -1597,7 +1647,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); @@ -1634,21 +1684,35 @@ 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); + } + } } @@ -1664,6 +1728,22 @@ public: typename std::set::const_iterator cc = c; for (++cc; cc != sc->end (); ++cc) { mp_cell_clusters->join_cluster_with (*c, *cc); + m_soft_connections.erase (std::make_pair (*c < *cc ? *c : *cc, *c < *cc ? *cc : *c)); + } + + } + + 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); } } @@ -1685,8 +1765,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; @@ -1762,10 +1843,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; @@ -1832,23 +1913,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 (); @@ -1860,11 +1941,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 (); @@ -1889,7 +1970,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 (); @@ -1903,9 +1984,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); @@ -1917,13 +1998,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; } @@ -1935,7 +2016,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; @@ -1947,7 +2028,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) { @@ -1962,8 +2043,7 @@ private: int soft = 0; if ((! m_separate_attributes || i->same_attrs (*j)) && i->interacts (*j, t21, *mp_conn, soft)) { - // @@@ soft mode? - new_interactions.push_back (std::make_pair (i->id (), j->id ())); + new_interactions.push_back (ClusterIDPair (i->id (), j->id (), soft)); } } @@ -2015,22 +2095,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 ()); @@ -2051,6 +2131,7 @@ private: } } + } /** @@ -2077,7 +2158,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) { @@ -2090,9 +2171,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 @@ -2124,7 +2205,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; @@ -2136,11 +2217,7 @@ private: int soft = 0; if ((! m_separate_attributes || c1.same_attrs (*j)) && c1.interacts (*j, t2, *mp_conn, soft)) { - if (soft != 0) { - // @@@ TODO: soft mode - } else { - interactions.push_back (std::make_pair (c1.id (), j->id ())); - } + interactions.push_back (ClusterIDPair (c1.id (), j->id (), soft)); } } @@ -2243,8 +2320,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); @@ -2254,34 +2331,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 diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index e0c51fd53..62b060644 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -622,6 +622,11 @@ 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 Gets the number of clusters */ @@ -844,7 +849,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) { @@ -1156,7 +1215,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) From dca287b72442df1d80b51e56a594d3c6a59b314d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 14 Mar 2024 01:02:56 +0100 Subject: [PATCH 09/42] WIP --- src/db/db/dbHierNetworkProcessor.cc | 85 ++++++++++++++++++++++++++++- src/db/db/dbHierNetworkProcessor.h | 14 +++++ 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 8377e3c52..247c9cf4c 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -809,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 @@ -846,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_connection_for (id); } template @@ -866,6 +871,10 @@ local_clusters::join_cluster_with (typename local_cluster::id_type id, typ // we just clear them. with->clear (); + // take care of soft connections + remove_soft_connection_for (id, with_id); + remove_soft_connection_for (with_id); + m_needs_update = true; } @@ -883,7 +892,81 @@ template void local_clusters::make_soft_connection (typename local_cluster::id_type a, typename local_cluster::id_type b) { - // @@@ TODO: implement + // 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; + } +} + +template +void +local_clusters::remove_soft_connection_for (typename local_cluster::id_type a, typename local_cluster::id_type b) +{ + auto sc = m_soft_connections.find (a); + if (sc != m_soft_connections.end ()) { + sc->second.erase (b); + if (sc->second.empty ()) { + m_soft_connections.erase (sc); + } + } + + sc = m_soft_connections_rev.find (b); + if (sc != m_soft_connections_rev.end ()) { + sc->second.erase (a); + if (sc->second.empty ()) { + m_soft_connections_rev.erase (sc); + } + } +} + +template +void +local_clusters::remove_soft_connection_for (typename local_cluster::id_type id) +{ + auto sc = m_soft_connections.find (id); + if (sc != m_soft_connections.end ()) { + + for (auto i = sc->second.begin (); i != sc->second.end (); ++i) { + auto sc_rev = m_soft_connections_rev.find (*i); + tl_assert (sc_rev != m_soft_connections_rev.end ()) { + sc_rev->second.erase (id); + if (sc_rev->second.empty ()) { + m_soft_connections_rev.erase (*i); + } + } + } + + m_soft_connections.erase (sc); + + } } template diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 62b060644..4b5cb5679 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -627,6 +627,16 @@ public: */ 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 */ @@ -647,8 +657,12 @@ 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_connection_for (typename local_cluster::id_type a, typename local_cluster::id_type b); + void remove_soft_connection_for (typename local_cluster::id_type id); }; /** From c4fb287eb7e4fe14fdf352918814298d97dd124a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 14 Mar 2024 19:36:19 +0100 Subject: [PATCH 10/42] [consider merging] robustness of cross reference view against temporary loss of xref object reference --- .../layui/layNetlistCrossReferenceModel.cc | 96 ++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) 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); From befbde0f43ce69ad275c2480625f30307fe46559 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 14 Mar 2024 22:14:20 +0100 Subject: [PATCH 11/42] WIP --- src/db/db/dbHierNetworkProcessor.cc | 40 +++++++++++++++++++++------- src/db/db/dbHierNetworkProcessor.h | 2 +- src/db/db/dbLayoutToNetlist.cc | 41 ++++++++++++++++++++++++++++- src/db/db/dbLayoutToNetlist.h | 1 + src/db/db/dbNetlistExtractor.cc | 4 ++- 5 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 247c9cf4c..d44349231 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1001,6 +1001,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 @@ -1268,6 +1270,10 @@ private: 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 ()); @@ -1280,21 +1286,35 @@ private: m_clusters.erase (ic2); - auto i1 = m_soft_connections.find (std::make_pair (ic1.operator-> (), ic2.operator-> ())); - if (i1 != m_soft_connections.end ()) { - i1->second = 0; + // 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)); } - auto i2 = m_soft_connections.find (std::make_pair (ic2.operator-> (), ic1.operator-> ())); - if (i2 != m_soft_connections.end ()) { - i2->second = 0; + 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) { - auto i1 = m_soft_connections.find (std::make_pair (ic1.operator-> (), ic2.operator-> ())); - auto i2 = m_soft_connections.find (std::make_pair (ic2.operator-> (), ic1.operator-> ())); + 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; @@ -1305,8 +1325,8 @@ private: if (i1 == m_soft_connections.end ()) { tl_assert (i2 == m_soft_connections.end ()); - m_soft_connections.insert (std::make_pair (std::make_pair (ic1.operator-> (), ic2.operator-> ()), soft)); - m_soft_connections.insert (std::make_pair (std::make_pair (ic2.operator-> (), ic1.operator-> ()), -soft)); + 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)); } } }; diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 4b5cb5679..efec69dd5 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -338,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 { diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 4bf49e1d3..1795c4cdb 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -33,6 +33,7 @@ #include "dbLayoutToNetlistFormatDefs.h" #include "dbLayoutVsSchematicFormatDefs.h" #include "dbShapeProcessor.h" +#include "dbNetlistDeviceClasses.h" #include "dbLog.h" #include "tlGlobPattern.h" @@ -425,7 +426,9 @@ void LayoutToNetlist::extract_netlist () netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters); // @@@ NOTE: can we have multiple pins on a net? Will this happen later maybe? - // @@@ do_soft_connections () + tl_assert (check_many_pins (mp_netlist.get ())); // @@@ + do_soft_connections (); + tl_assert (check_many_pins (mp_netlist.get ())); // @@@ do_join_nets (); tl_assert (check_many_pins (mp_netlist.get ())); // @@@ @@ -640,6 +643,42 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N } } +void LayoutToNetlist::do_soft_connections () +{ + +// @@@ NetlistLocker locked_netlist (mp_netlist.get ()); + + db::DeviceClassDiode *soft_diode = new db::DeviceClassDiode (); + soft_diode->set_name ("SOFT"); + mp_netlist->add_device_class (soft_diode); + + for (auto c = mp_netlist->begin_bottom_up (); c != mp_netlist->end_bottom_up (); ++c) { + + // @@@ create diodes as of now + + 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); + } + + } + + } + + } +} + void LayoutToNetlist::do_join_nets () { if (! mp_netlist) { diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index e18a11e5d..01f945e97 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -1093,6 +1093,7 @@ private: 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); 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; From 92f887c3195fd122d77cf9433b4d3f4f0e53f90d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 14 Mar 2024 23:27:06 +0100 Subject: [PATCH 12/42] WIP --- src/db/db/dbHierNetworkProcessor.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index d44349231..eee016493 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1225,6 +1225,7 @@ struct cluster_building_receiver 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); From 5fbb57750b9aa526910d5765629e94caebc9dcd9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 15 Mar 2024 18:47:08 +0100 Subject: [PATCH 13/42] WIP --- src/db/db/dbHierNetworkProcessor.cc | 80 ++++++++++++++--------------- src/db/db/dbHierNetworkProcessor.h | 3 +- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index eee016493..6ed288b23 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -850,7 +850,7 @@ local_clusters::remove_cluster (typename local_cluster::id_type id) m_needs_update = true; // take care of soft connections - remove_soft_connection_for (id); + remove_soft_connections_for (id); } template @@ -872,8 +872,18 @@ local_clusters::join_cluster_with (typename local_cluster::id_type id, typ with->clear (); // take care of soft connections - remove_soft_connection_for (id, with_id); - remove_soft_connection_for (with_id); + + 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; } @@ -892,6 +902,10 @@ 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); @@ -926,47 +940,33 @@ local_clusters::upward_soft_connections (typename local_cluster::id_type i } } -template -void -local_clusters::remove_soft_connection_for (typename local_cluster::id_type a, typename local_cluster::id_type b) +static void +remove_id_from_map (std::map > &fwd, std::map > &rev, size_t id) { - auto sc = m_soft_connections.find (a); - if (sc != m_soft_connections.end ()) { - sc->second.erase (b); - if (sc->second.empty ()) { - m_soft_connections.erase (sc); - } - } + auto sc = fwd.find (id); + if (sc != fwd.end ()) { - sc = m_soft_connections_rev.find (b); - if (sc != m_soft_connections_rev.end ()) { - sc->second.erase (a); - if (sc->second.empty ()) { - m_soft_connections_rev.erase (sc); + 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_connection_for (typename local_cluster::id_type id) +local_clusters::remove_soft_connections_for (typename local_cluster::id_type id) { - auto sc = m_soft_connections.find (id); - if (sc != m_soft_connections.end ()) { - - for (auto i = sc->second.begin (); i != sc->second.end (); ++i) { - auto sc_rev = m_soft_connections_rev.find (*i); - tl_assert (sc_rev != m_soft_connections_rev.end ()) { - sc_rev->second.erase (id); - if (sc_rev->second.empty ()) { - m_soft_connections_rev.erase (*i); - } - } - } - - m_soft_connections.erase (sc); - - } + 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 @@ -1034,7 +1034,7 @@ struct cluster_building_receiver void generate_clusters (local_clusters &clusters) { - std::map *> in_to_out; + 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) { @@ -1046,7 +1046,7 @@ struct cluster_building_receiver cluster->add_attr (s->second.second); } - in_to_out.insert (std::make_pair (c.operator-> (), cluster)); + in_to_out.insert (std::make_pair (c.operator-> (), cluster->id ())); std::set global_nets = c->second; @@ -1087,7 +1087,7 @@ struct cluster_building_receiver 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->id (), c2->second->id ()); + clusters.make_soft_connection (c1->second, c2->second); } } @@ -2957,7 +2957,7 @@ 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 ()); + local.remove_cluster (i->id ()); // @@@ actually required? } else { @@ -2968,7 +2968,7 @@ 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); + local.remove_cluster (other_id); // @@@ actually required? } else { local.add_connection (gcid, *i); } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index efec69dd5..08ec80718 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -661,8 +661,7 @@ private: std::map > m_soft_connections_rev; void apply_attr_equivalences (const tl::equivalence_clusters &attr_equivalence); - void remove_soft_connection_for (typename local_cluster::id_type a, typename local_cluster::id_type b); - void remove_soft_connection_for (typename local_cluster::id_type id); + void remove_soft_connections_for (typename local_cluster::id_type id); }; /** From 417309acf1b4d3eb02fe3ad3e8454a926f931f95 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 15 Mar 2024 23:15:21 +0100 Subject: [PATCH 14/42] Added a basic test --- .../unit_tests/dbHierNetworkProcessorTests.cc | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 6c771ec19..60017754d 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -393,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; } @@ -670,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; From 44edd8d26a576966016ab9cd0b1cedbc48223cd9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 16 Mar 2024 00:02:44 +0100 Subject: [PATCH 15/42] Added a regression test for netlist extractor with soft connections --- src/db/unit_tests/dbNetlistExtractorTests.cc | 339 +++++++++++++++++++ testdata/algo/soft_connections.gds | Bin 0 -> 13710 bytes testdata/algo/soft_connections_au.gds | Bin 0 -> 26174 bytes 3 files changed, 339 insertions(+) create mode 100644 testdata/algo/soft_connections.gds create mode 100644 testdata/algo/soft_connections_au.gds diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 916169102..494e8dc32 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,336 @@ 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 +#if 0 // @@@ + 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); +#endif + + 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/testdata/algo/soft_connections.gds b/testdata/algo/soft_connections.gds new file mode 100644 index 0000000000000000000000000000000000000000..7a92fe3282b263ffbd0e1466d2fc40ec72c294a7 GIT binary patch literal 13710 zcmdU$Ux=MY8OG=QKYR9UcC%Ttf4ad`1w#pHW7JeiP1a;}<92H|+qLE*EX_8AQc95` zB1J?*Vil=UO3@M)ZxksRO3%zPuwb(+nU)5jjZ+n9RWnC4j3G)!|})fjVhqGn9v=f;d4 zbvpS9F8GNtQrV&P{RzJMcgIu7)&5#2dEcJqp8@|PTnFv`0sQqsWrrsD#xY~YJLo@j z;`8YLg+gVACVBR+@q=DJZU5PCjze~6DbI29K6B4HPAWU}1LHY=^h2dz&Yx8FGXL)f z&p2rv(Lduym0j^0fqAT2`;T9L%{#dY+v8Y;m1oTqS0g{|7mOL%uxg_=9Mzq76f}bx z-*OZ*gF5gDM?nWu^BUK|-#RK#OS@g!i;CTq6|CaUdBg=e_D^iR*9(;$n&j(Ee?4^n zBgV9zDb(K2wWs+xOw~2`g?2C}jm1J`hbDRUo7dTKfamV!eWcrt>eA z9a`$2*U>*Y`jyIF#y4*{|5S2qtW+s^-=6fp*Z%YVj1!e{qkpOFgW|{QhMzxE4W9}X z?%<~pKWGOhYvVhG$_@?2-_IZTqvL0vcf3?~=m*Bv-oX69Kb3yl#|xFc%>VnrGfun@ z<46Be+2i=(%HXM7`6bSZ3B-^3XRP0qKxLl@jrnIr{IZ>;w3jryM_v>&Z)HhbG6*{_{F2ImS;adl}DuD^7}=RQ9s?wI&@; zCD*!9D0$zW?7xkh;vT zwC<#`$Lr7Z@p`ymC6nc*I(cKYoBW0N@b7tZ|tAr=XF$ajK5U>!cc?epRGf7E~a&3pfe z`(MR*Gx{p}4efp#`|!Jk$_`D&uki$`3X9nH)OQh!j|D3GqT^kDYDWD24YS&#<~Xyz z*5kOtHK(j;wC{2?59%cTD*r1q&PsZBwOJv~BWB9>Q~y08w1E}kRd#4ND)p+zvo^rb zA*`Z0E*Q=&AWUVWjznofE7sEuY5Bx!l&NQbhtozMX@bB&& zjgz0XpW~*>^~vhO{9)6cZLXg_jsIk=FPE=5{@l4;@XU_Jx9I9K(b*q}nRb73n zP_B009`1^$0{vmCzz$c7WCgQ@MFh2@9*$xKlT>~MlN45PhA&nyNexxO>{Vu2xg-S* zW;3o_n&)vgp2v=W+DhYGp|Y1&E^&Qc38Ix0D4-cs2I@q5Xi$dG?;(1{$f6V{lq-|=gogVuXV0?O>j<)3MRyn=rjErx~5V4?`;g)7;Ty@N{sKFM#T*#dJ6MkPPTAeEnE_!+Q~8?J}UF-Ya-7^LDHW81gO*jv+ob9;mVac!KiQ$B{^u?`{tjfV$D!{zUMf4(@?Cez4pm3wvu@r! zBX(6sBwyS1jPdhdddf3?Jf*3@Cmet2Dc_iKJaza}o-;jjBEJ@Q=Kim*eG{D>?iA%Y z&u3@;Iq#piHVVV}8sBohpcxd;dHTvf=Sgil=c9Gbxzv8*-12&<>^b|4JogzjvCm4^ z89A;qYVb*{Gj|1Qy7nVZD!XdFgKM~}ODkV_00HQrz&5Gh^xFEpf4i~$Kx=!_y-CygrJ(fsZ9eZGZiJ+Xt$qV=Bh}Dstko zy~?b=eD*3aI6-wT>C;9GYutu@fsS_u$m!2$Cc4(3xxdQ)B zqdo%d{t3ql_kma0p-KMYhVxIQ|IMvJ>Hp$}ZBO#&@G`{fsN~m{3zc2{AMoCE$$l=o zZ}k|v`PHoNU;B9EF#P_u_x)?zy|)GLJ;t81eD3hP<#UMVyHs{Pk38QgzQaVUAIJFb zbvjYU{2j;my~+*^;}`M#1=uKjQ@h{C{Qb00*`Z1P;)~8dm3}uj3#EVGp5)Jc)$vsF zYZHZ%_wAN9}RnALw-4ZVsw%5T@Z2<5)lobm}rPAWSz$&YLx zYHNrZv?^iv>-}~k|`KyCX tW8=2dxBf%%og;|DJm0_1tejd{KI0;g(*s5mGl*~g7k##ex3GHM{2%vo?;QXD literal 0 HcmV?d00001 diff --git a/testdata/algo/soft_connections_au.gds b/testdata/algo/soft_connections_au.gds new file mode 100644 index 0000000000000000000000000000000000000000..8a34b60f76bd60c91667e4ae3e956fc4ae04a62e GIT binary patch literal 26174 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 H*jVykD~3K! literal 0 HcmV?d00001 From 5e95b32ac4fd80312a0a9279e4aa310f28399f66 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 16 Mar 2024 00:10:35 +0100 Subject: [PATCH 16/42] WIP --- src/db/db/dbLayoutToNetlist.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 1795c4cdb..cf70d975a 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -645,12 +645,9 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N void LayoutToNetlist::do_soft_connections () { + // @@@ NetlistLocker locked_netlist (mp_netlist.get ()); -// @@@ NetlistLocker locked_netlist (mp_netlist.get ()); - - db::DeviceClassDiode *soft_diode = new db::DeviceClassDiode (); - soft_diode->set_name ("SOFT"); - mp_netlist->add_device_class (soft_diode); + db::DeviceClassDiode *soft_diode = 0; for (auto c = mp_netlist->begin_bottom_up (); c != mp_netlist->end_bottom_up (); ++c) { @@ -663,6 +660,12 @@ void LayoutToNetlist::do_soft_connections () 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); From db2ae89521422a9fc58916ebfeb5671bc12824d0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 16 Mar 2024 18:19:20 +0100 Subject: [PATCH 17/42] WIP --- src/db/db/dbLayoutToNetlist.cc | 376 +++++++++++++++++++++++++- src/db/db/dbLayoutToNetlist.h | 16 ++ src/db/db/gsiDeclDbLayoutToNetlist.cc | 6 + 3 files changed, 388 insertions(+), 10 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index cf70d975a..aff06cb31 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -46,7 +46,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 ()) { @@ -66,7 +66,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 ()); @@ -74,7 +74,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 ()); @@ -85,7 +85,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 (); } @@ -427,8 +427,16 @@ void LayoutToNetlist::extract_netlist () // @@@ NOTE: can we have multiple pins on a net? Will this happen later maybe? tl_assert (check_many_pins (mp_netlist.get ())); // @@@ - do_soft_connections (); + + // treat soft connections + if (m_make_soft_connection_diodes) { + make_soft_connection_diodes (); + } else { + do_soft_connections (); + } + tl_assert (check_many_pins (mp_netlist.get ())); // @@@ + // implement the "join_nets" (aka "must connect") feature do_join_nets (); tl_assert (check_many_pins (mp_netlist.get ())); // @@@ @@ -643,16 +651,12 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N } } -void LayoutToNetlist::do_soft_connections () +void LayoutToNetlist::make_soft_connection_diodes () { - // @@@ NetlistLocker locked_netlist (mp_netlist.get ()); - db::DeviceClassDiode *soft_diode = 0; for (auto c = mp_netlist->begin_bottom_up (); c != mp_netlist->end_bottom_up (); ++c) { - // @@@ create diodes as of now - auto clusters = net_clusters ().clusters_per_cell (c->cell_index ()); for (auto n = c->begin_nets (); n != c->end_nets (); ++n) { @@ -682,6 +686,358 @@ void LayoutToNetlist::do_soft_connections () } } +namespace +{ + + /** + * @brief Describes a soft-connected cluster + * + * Such a cluster 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. + */ + class SoftConnectionClusterInfo + { + public: + typedef std::set pin_set; + typedef pin_set::const_iterator pin_iterator; + typedef std::map dir_map; + + SoftConnectionClusterInfo () + : m_partial_net_count (0) + { + // .. nothing yet .. + } + + /** + * @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 (0: no soft connection or both directions, +1: up-only, -1: down-only) + * @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, int 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 < 0 && ! net->is_floating ()) { + m_partial_net_count += 1; + } + + if (pin) { + m_pin_ids.insert (pin->id ()); + } + + m_cluster_dir.insert (std::make_pair (net->cluster_id (), dir)); + } + + /** + * @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 pins on the cluster (begin iterator) + * + * The iterator delivers Pin IDs + */ + pin_iterator begin_pins () const + { + return m_pin_ids.begin (); + } + + /** + * @brief Gets the pins on the cluster (end iterator) + */ + pin_iterator end_pins () const + { + return m_pin_ids.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-clusters that are formed inside + * the circuit and how these clusters connect to pins. + */ + class SoftConnectionCircuitInfo + { + public: + /** + * @brief Creates a new cluster info object + */ + SoftConnectionClusterInfo &make_cluster () + { + m_cluster_info.push_back (SoftConnectionClusterInfo ()); + return m_cluster_info.back (); + } + + /** + * @brief Adds information about a pin + * + * @param pin The pin + * @param dir The nature of connections from the pin: 0 if no soft connections / both directions, +1 to "up only" and -1 for "down only" + * @param sc_cluster_info The soft-connected net cluster info object + */ + void add_pin_info (const db::Pin *pin, int dir, SoftConnectionClusterInfo *sc_cluster_info) + { + if (pin) { + m_pin_info.insert (std::make_pair (pin->id (), std::make_pair (dir, sc_cluster_info))); + } + } + + /** + * @brief Gets the direction attribute of the pin + */ + int direction_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.first : 0; + } + + /** + * @brief Gets the soft-connected net cluster info object the pin connects to + */ + const SoftConnectionClusterInfo *get_cluster_info_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; + } + + private: + std::list m_cluster_info; + std::map > m_pin_info; + }; + + /** + * @brief Provides temporary soft connection information for a netlist + */ + class SoftConnectionInfo + { + public: + SoftConnectionInfo () + { + // .. nothing yet .. + } + + /** + * @brief Builds the soft connection information for the given netlist and net clusters + */ + void build (const db::Netlist &netlist, const db::hier_clusters &net_clusters) + { + for (auto c = netlist.begin_bottom_up (); c != netlist.end_bottom_up (); ++c) { + build_clusters_for_circuit (c.operator-> (), net_clusters.clusters_per_cell (c->cell_index ())); + } + + } + + private: + /** + * @brief Builds the per-circuit cluster information + * + * First of all, this method creates a SoftConnectionCircuitInfo object for the circuit. + * + * Inside this per-circuit object, it will create a number of SoftConnectionClusterInfo 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_clusters_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 ())).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 ()); + + SoftConnectionClusterInfo *sc_cluster_info = 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); + + // 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: + + int dir = 0; + + // 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 ? 1 : -1; + } + 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 && 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_cluster_info) { + sc_cluster_info = &sc_circuit_info.make_cluster (); + } + + sc_cluster_info->add (net, dir, pin, sc_partial_net_count); + + sc_circuit_info.add_pin_info (pin, dir, sc_cluster_info); + + } + + connected.swap (next_connected); + + } + + } + + } + + /** + * @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) + { + int look_for_dir = up ? 1 : -1; + + 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 ()) { + int dir = scc_ref->second.direction_per_pin (pin); + if (dir == look_for_dir) { + return true; + } + } + } + + return false; + } + + /** + * @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) + { + auto scc = m_scc_per_circuit.find (subcircuit->circuit_ref ()); + if (scc != m_scc_per_circuit.end ()) { + + const SoftConnectionCircuitInfo &sci = scc->second; + + const SoftConnectionClusterInfo *scci = sci.get_cluster_info_per_pin (pin); + if (scci) { + + partial_net_count += 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 ()); + } + } + } + + } + + } + } + + /** + * @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) + { + 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; + } + + std::map m_scc_per_circuit; + }; + +} + +void LayoutToNetlist::do_soft_connections () +{ + + // @@@ + +} + void LayoutToNetlist::do_join_nets () { if (! mp_netlist) { diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 01f945e97..c6630d13b 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -1045,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); @@ -1074,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; @@ -1099,6 +1112,9 @@ private: 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 make_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/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index e8f12cddf..a9c06d59d 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -506,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" From 9ab7a5a84f84f5f69ea8af87b5eeefe9fe7d3fa3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 16 Mar 2024 21:38:35 +0100 Subject: [PATCH 18/42] Soft connection analysis, first algorithm --- src/db/db/dbLayoutToNetlist.cc | 180 +++++++++++++++++++++++++++++++-- src/db/db/dbLayoutToNetlist.h | 2 +- 2 files changed, 171 insertions(+), 11 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index aff06cb31..f80f1e7d2 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -430,7 +430,7 @@ void LayoutToNetlist::extract_netlist () // treat soft connections if (m_make_soft_connection_diodes) { - make_soft_connection_diodes (); + do_make_soft_connection_diodes (); } else { do_soft_connections (); } @@ -651,7 +651,7 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N } } -void LayoutToNetlist::make_soft_connection_diodes () +void LayoutToNetlist::do_make_soft_connection_diodes () { db::DeviceClassDiode *soft_diode = 0; @@ -702,6 +702,7 @@ namespace 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; SoftConnectionClusterInfo () : m_partial_net_count (0) @@ -719,10 +720,12 @@ namespace */ void add (const db::Net *net, int dir, const db::Pin *pin, size_t partial_net_count) { - m_partial_net_count += partial_net_count; + // limiting the partial net count to 1 means we report errors only once in the + // hierarchy, not on every level + m_partial_net_count += std::min (size_t (1), partial_net_count); // this is where we make the decision about the partial nets ... - if (! pin && dir < 0 && ! net->is_floating ()) { + if (! pin && dir < 0) { m_partial_net_count += 1; } @@ -752,7 +755,7 @@ namespace /** * @brief Gets the pins on the cluster (begin iterator) * - * The iterator delivers Pin IDs + * The iterator delivers Pin IDs of pins leading outside the circuit this cluster lives in. */ pin_iterator begin_pins () const { @@ -767,6 +770,22 @@ namespace 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; @@ -782,6 +801,26 @@ namespace class SoftConnectionCircuitInfo { public: + typedef std::list cluster_list; + typedef cluster_list::const_iterator cluster_list_iterator; + + /** + * @brief Constructor + */ + SoftConnectionCircuitInfo (const db::Circuit *circuit) + : mp_circuit (circuit) + { + // .. nothing yet .. + } + + /** + * @brief Gets the circuit for this info object + */ + const db::Circuit *circuit () const + { + return mp_circuit; + } + /** * @brief Creates a new cluster info object */ @@ -831,8 +870,25 @@ namespace return p != m_pin_info.end () ? p->second.second : 0; } + /** + * @brief List of per-circui info objects, begin iterator + */ + cluster_list_iterator begin () const + { + return m_cluster_info.begin (); + } + + /** + * @brief List of per-circui info objects, end iterator + */ + cluster_list_iterator end () const + { + return m_cluster_info.end (); + } + private: - std::list m_cluster_info; + const db::Circuit *mp_circuit; + cluster_list m_cluster_info; std::map > m_pin_info; }; @@ -855,7 +911,100 @@ namespace for (auto c = netlist.begin_bottom_up (); c != netlist.end_bottom_up (); ++c) { build_clusters_for_circuit (c.operator-> (), net_clusters.clusters_per_cell (c->cell_index ())); } + } + /** + * @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) + { + if (tl::verbosity () >= 20) { + tl::info << "Joining soft-connected net clusters .."; + } + + size_t nclusters_tot = 0; + size_t npartial_tot = 0; + + for (auto c = netlist.begin_top_down (); c != netlist.end_top_down (); ++c) { + + size_t nclusters = 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); + ++nclusters; + while (++cc != sc->end_clusters ()) { + // TODO: logging? + c->join_nets (net0, c->net_by_cluster_id (cc->first)); + ++npartial; + } + } + + } + + nclusters_tot += nclusters; + npartial_tot += npartial; + + if (nclusters > 0 && tl::verbosity () >= 30) { + tl::info << "Circuit " << c->name () << ": joined " << nclusters << " soft-connected net clusters with " << npartial << " partial nets."; + } + + } + + if (tl::verbosity () >= 20) { + tl::info << "Joined " << nclusters_tot << " soft-connected net clusters with " << npartial_tot << " partial nets in total."; + } + + m_scc_per_circuit.clear (); + } + + void print_errors (const db::Netlist &netlist) + { + 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; + + bool first = true; + for (auto sc = sc_info.begin (); sc != sc_info.end (); ++sc) { + + if (sc->partial_net_count () < 2) { + continue; + } + + if (first) { + tl::info << "Circuit " << c->name () << ":"; + first = false; + } + + tl::info << " Partial nets on soft-connect cluster:"; + for (auto cc = sc->begin_clusters (); cc != sc->end_clusters (); ++cc) { + if (cc->second < 0) { + tl::info << " " << c->net_by_cluster_id (cc->first)->expanded_name (); + } + } + + } + + } } private: @@ -871,7 +1020,7 @@ namespace */ void build_clusters_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 ())).first->second; + 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) { @@ -894,7 +1043,10 @@ namespace for (auto cc = connected.begin (); cc != connected.end (); ++cc) { - const db::Net *net = circuit->net_by_cluster_id (*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: @@ -929,7 +1081,7 @@ namespace // is this net associated with a pin? const db::Pin *pin = 0; - if (net && net->begin_pins () != net->end_pins ()) { + 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 (); @@ -939,7 +1091,10 @@ namespace sc_cluster_info = &sc_circuit_info.make_cluster (); } - sc_cluster_info->add (net, dir, pin, sc_partial_net_count); + // we do not count floating nets as they cannot make a functional connection + if (! net->is_floating ()) { + sc_cluster_info->add (net, dir, pin, sc_partial_net_count); + } sc_circuit_info.add_pin_info (pin, dir, sc_cluster_info); @@ -1033,9 +1188,14 @@ namespace void LayoutToNetlist::do_soft_connections () { + SoftConnectionInfo sc_info; + sc_info.build (*netlist (), net_clusters ()); + // @@@ + sc_info.print_errors (*netlist ()); // @@@ + sc_info.join_soft_connections (*netlist ()); } void LayoutToNetlist::do_join_nets () diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index c6630d13b..075804df1 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -1113,7 +1113,7 @@ private: 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 make_soft_connection_diodes (); + void do_make_soft_connection_diodes (); // implementation of NetlistManipulationCallbacks virtual size_t link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &trans); From b9baa24d3a4f3127cc0b019dd2c872abe5b973ce Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 16 Mar 2024 21:56:39 +0100 Subject: [PATCH 19/42] Refactoring (split source code file) --- src/db/db/db.pro | 2 + src/db/db/dbLayoutToNetlist.cc | 501 +----------------- src/db/db/dbLayoutToNetlistSoftConnections.cc | 348 ++++++++++++ src/db/db/dbLayoutToNetlistSoftConnections.h | 269 ++++++++++ 4 files changed, 620 insertions(+), 500 deletions(-) create mode 100644 src/db/db/dbLayoutToNetlistSoftConnections.cc create mode 100644 src/db/db/dbLayoutToNetlistSoftConnections.h 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/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index f80f1e7d2..0b2ab5c55 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -32,6 +32,7 @@ #include "dbLayoutVsSchematic.h" #include "dbLayoutToNetlistFormatDefs.h" #include "dbLayoutVsSchematicFormatDefs.h" +#include "dbLayoutToNetlistSoftConnections.h" #include "dbShapeProcessor.h" #include "dbNetlistDeviceClasses.h" #include "dbLog.h" @@ -686,506 +687,6 @@ void LayoutToNetlist::do_make_soft_connection_diodes () } } -namespace -{ - - /** - * @brief Describes a soft-connected cluster - * - * Such a cluster 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. - */ - class SoftConnectionClusterInfo - { - 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; - - SoftConnectionClusterInfo () - : m_partial_net_count (0) - { - // .. nothing yet .. - } - - /** - * @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 (0: no soft connection or both directions, +1: up-only, -1: down-only) - * @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, int dir, const db::Pin *pin, size_t partial_net_count) - { - // limiting the partial net count to 1 means we report errors only once in the - // hierarchy, not on every level - m_partial_net_count += std::min (size_t (1), partial_net_count); - - // this is where we make the decision about the partial nets ... - if (! pin && dir < 0) { - m_partial_net_count += 1; - } - - if (pin) { - m_pin_ids.insert (pin->id ()); - } - - m_cluster_dir.insert (std::make_pair (net->cluster_id (), dir)); - } - - /** - * @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 pins on the cluster (begin iterator) - * - * The iterator delivers Pin IDs of pins leading outside the circuit this cluster lives in. - */ - pin_iterator begin_pins () const - { - return m_pin_ids.begin (); - } - - /** - * @brief Gets the pins on the cluster (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-clusters that are formed inside - * the circuit and how these clusters connect to pins. - */ - class SoftConnectionCircuitInfo - { - public: - typedef std::list cluster_list; - typedef cluster_list::const_iterator cluster_list_iterator; - - /** - * @brief Constructor - */ - SoftConnectionCircuitInfo (const db::Circuit *circuit) - : mp_circuit (circuit) - { - // .. nothing yet .. - } - - /** - * @brief Gets the circuit for this info object - */ - const db::Circuit *circuit () const - { - return mp_circuit; - } - - /** - * @brief Creates a new cluster info object - */ - SoftConnectionClusterInfo &make_cluster () - { - m_cluster_info.push_back (SoftConnectionClusterInfo ()); - return m_cluster_info.back (); - } - - /** - * @brief Adds information about a pin - * - * @param pin The pin - * @param dir The nature of connections from the pin: 0 if no soft connections / both directions, +1 to "up only" and -1 for "down only" - * @param sc_cluster_info The soft-connected net cluster info object - */ - void add_pin_info (const db::Pin *pin, int dir, SoftConnectionClusterInfo *sc_cluster_info) - { - if (pin) { - m_pin_info.insert (std::make_pair (pin->id (), std::make_pair (dir, sc_cluster_info))); - } - } - - /** - * @brief Gets the direction attribute of the pin - */ - int direction_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.first : 0; - } - - /** - * @brief Gets the soft-connected net cluster info object the pin connects to - */ - const SoftConnectionClusterInfo *get_cluster_info_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; - } - - /** - * @brief List of per-circui info objects, begin iterator - */ - cluster_list_iterator begin () const - { - return m_cluster_info.begin (); - } - - /** - * @brief List of per-circui info objects, end iterator - */ - cluster_list_iterator end () const - { - return m_cluster_info.end (); - } - - private: - const db::Circuit *mp_circuit; - cluster_list m_cluster_info; - std::map > m_pin_info; - }; - - /** - * @brief Provides temporary soft connection information for a netlist - */ - class SoftConnectionInfo - { - public: - SoftConnectionInfo () - { - // .. nothing yet .. - } - - /** - * @brief Builds the soft connection information for the given netlist and net clusters - */ - void build (const db::Netlist &netlist, const db::hier_clusters &net_clusters) - { - for (auto c = netlist.begin_bottom_up (); c != netlist.end_bottom_up (); ++c) { - build_clusters_for_circuit (c.operator-> (), net_clusters.clusters_per_cell (c->cell_index ())); - } - } - - /** - * @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) - { - if (tl::verbosity () >= 20) { - tl::info << "Joining soft-connected net clusters .."; - } - - size_t nclusters_tot = 0; - size_t npartial_tot = 0; - - for (auto c = netlist.begin_top_down (); c != netlist.end_top_down (); ++c) { - - size_t nclusters = 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); - ++nclusters; - while (++cc != sc->end_clusters ()) { - // TODO: logging? - c->join_nets (net0, c->net_by_cluster_id (cc->first)); - ++npartial; - } - } - - } - - nclusters_tot += nclusters; - npartial_tot += npartial; - - if (nclusters > 0 && tl::verbosity () >= 30) { - tl::info << "Circuit " << c->name () << ": joined " << nclusters << " soft-connected net clusters with " << npartial << " partial nets."; - } - - } - - if (tl::verbosity () >= 20) { - tl::info << "Joined " << nclusters_tot << " soft-connected net clusters with " << npartial_tot << " partial nets in total."; - } - - m_scc_per_circuit.clear (); - } - - void print_errors (const db::Netlist &netlist) - { - 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; - - bool first = true; - for (auto sc = sc_info.begin (); sc != sc_info.end (); ++sc) { - - if (sc->partial_net_count () < 2) { - continue; - } - - if (first) { - tl::info << "Circuit " << c->name () << ":"; - first = false; - } - - tl::info << " Partial nets on soft-connect cluster:"; - for (auto cc = sc->begin_clusters (); cc != sc->end_clusters (); ++cc) { - if (cc->second < 0) { - tl::info << " " << c->net_by_cluster_id (cc->first)->expanded_name (); - } - } - - } - - } - } - - private: - /** - * @brief Builds the per-circuit cluster information - * - * First of all, this method creates a SoftConnectionCircuitInfo object for the circuit. - * - * Inside this per-circuit object, it will create a number of SoftConnectionClusterInfo 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_clusters_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 ()); - - SoftConnectionClusterInfo *sc_cluster_info = 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: - - int dir = 0; - - // 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 ? 1 : -1; - } - 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_cluster_info) { - sc_cluster_info = &sc_circuit_info.make_cluster (); - } - - // we do not count floating nets as they cannot make a functional connection - if (! net->is_floating ()) { - sc_cluster_info->add (net, dir, pin, sc_partial_net_count); - } - - sc_circuit_info.add_pin_info (pin, dir, sc_cluster_info); - - } - - connected.swap (next_connected); - - } - - } - - } - - /** - * @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) - { - int look_for_dir = up ? 1 : -1; - - 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 ()) { - int dir = scc_ref->second.direction_per_pin (pin); - if (dir == look_for_dir) { - return true; - } - } - } - - return false; - } - - /** - * @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) - { - auto scc = m_scc_per_circuit.find (subcircuit->circuit_ref ()); - if (scc != m_scc_per_circuit.end ()) { - - const SoftConnectionCircuitInfo &sci = scc->second; - - const SoftConnectionClusterInfo *scci = sci.get_cluster_info_per_pin (pin); - if (scci) { - - partial_net_count += 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 ()); - } - } - } - - } - - } - } - - /** - * @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) - { - 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; - } - - std::map m_scc_per_circuit; - }; - -} - void LayoutToNetlist::do_soft_connections () { SoftConnectionInfo sc_info; diff --git a/src/db/db/dbLayoutToNetlistSoftConnections.cc b/src/db/db/dbLayoutToNetlistSoftConnections.cc new file mode 100644 index 000000000..0380d4dca --- /dev/null +++ b/src/db/db/dbLayoutToNetlistSoftConnections.cc @@ -0,0 +1,348 @@ + + +/* + + 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 "dbNetlist.h" + +namespace db +{ + +// ------------------------------------------------------------------------------- +// SoftConnectionClusterInfo implementation + +SoftConnectionClusterInfo::SoftConnectionClusterInfo () + : m_partial_net_count (0) +{ + // .. nothing yet .. +} + +void SoftConnectionClusterInfo::add (const db::Net *net, int dir, const db::Pin *pin, size_t partial_net_count) +{ + // limiting the partial net count to 1 means we report errors only once in the + // hierarchy, not on every level + m_partial_net_count += std::min (size_t (1), partial_net_count); + + // this is where we make the decision about the partial nets ... + if (! pin && dir < 0) { + 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 .. +} + +SoftConnectionClusterInfo &SoftConnectionCircuitInfo::make_cluster () +{ + m_cluster_info.push_back (SoftConnectionClusterInfo ()); + return m_cluster_info.back (); +} + +void SoftConnectionCircuitInfo::add_pin_info (const db::Pin *pin, int dir, SoftConnectionClusterInfo *sc_cluster_info) +{ + if (pin) { + m_pin_info.insert (std::make_pair (pin->id (), std::make_pair (dir, sc_cluster_info))); + } +} + +int SoftConnectionCircuitInfo::direction_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.first : 0; +} + +const SoftConnectionClusterInfo *SoftConnectionCircuitInfo::get_cluster_info_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 &net_clusters) +{ + for (auto c = netlist.begin_bottom_up (); c != netlist.end_bottom_up (); ++c) { + build_clusters_for_circuit (c.operator-> (), net_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 clusters .."; + } + + size_t nclusters_tot = 0; + size_t npartial_tot = 0; + + for (auto c = netlist.begin_top_down (); c != netlist.end_top_down (); ++c) { + + size_t nclusters = 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); + ++nclusters; + while (++cc != sc->end_clusters ()) { + // TODO: logging? + c->join_nets (net0, c->net_by_cluster_id (cc->first)); + ++npartial; + } + } + + } + + nclusters_tot += nclusters; + npartial_tot += npartial; + + if (nclusters > 0 && tl::verbosity () >= 30) { + tl::info << "Circuit " << c->name () << ": joined " << nclusters << " soft-connected net clusters with " << npartial << " partial nets."; + } + + } + + if (tl::verbosity () >= 20) { + tl::info << "Joined " << nclusters_tot << " soft-connected net clusters with " << npartial_tot << " partial nets in total."; + } + + m_scc_per_circuit.clear (); +} + +void SoftConnectionInfo::print_errors (const db::Netlist &netlist) +{ + 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; + + bool first = true; + for (auto sc = sc_info.begin (); sc != sc_info.end (); ++sc) { + + if (sc->partial_net_count () < 2) { + continue; + } + + if (first) { + tl::info << "Circuit " << c->name () << ":"; + first = false; + } + + tl::info << " Partial nets on soft-connect cluster:"; + for (auto cc = sc->begin_clusters (); cc != sc->end_clusters (); ++cc) { + if (cc->second < 0) { + tl::info << " " << c->net_by_cluster_id (cc->first)->expanded_name (); + } + } + + } + + } +} + +void SoftConnectionInfo::build_clusters_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 ()); + + SoftConnectionClusterInfo *sc_cluster_info = 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: + + int dir = 0; + + // 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 ? 1 : -1; + } + 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_cluster_info) { + sc_cluster_info = &sc_circuit_info.make_cluster (); + } + + // we do not count floating nets as they cannot make a functional connection + if (! net->is_floating ()) { + sc_cluster_info->add (net, dir, pin, sc_partial_net_count); + } + + sc_circuit_info.add_pin_info (pin, dir, sc_cluster_info); + + } + + connected.swap (next_connected); + + } + + } + +} + +bool SoftConnectionInfo::net_has_up_or_down_subcircuit_connections (const db::Net *net, bool up) +{ + int look_for_dir = up ? 1 : -1; + + 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 ()) { + int 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 SoftConnectionClusterInfo *scci = sci.get_cluster_info_per_pin (pin); + if (scci) { + + partial_net_count += 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..9c2bad69d --- /dev/null +++ b/src/db/db/dbLayoutToNetlistSoftConnections.h @@ -0,0 +1,269 @@ + +/* + + 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 +#include +#include +#include + +namespace db +{ + +class Net; +class Pin; +class Circuit; +class Netlist; +class SubCircuit; + +template class hier_clusters; +template class connected_clusters; +class NetShape; + +/** + * @brief Describes a soft-connected cluster + * + * Such a cluster 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. + */ +class DB_PUBLIC SoftConnectionClusterInfo +{ +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; + + SoftConnectionClusterInfo (); + + /** + * @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 (0: no soft connection or both directions, +1: up-only, -1: down-only) + * @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, int 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 pins on the cluster (begin iterator) + * + * The iterator delivers Pin IDs of pins leading outside the circuit this cluster lives in. + */ + pin_iterator begin_pins () const + { + return m_pin_ids.begin (); + } + + /** + * @brief Gets the pins on the cluster (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-clusters that are formed inside + * the circuit and how these clusters connect to pins. + */ +class DB_PUBLIC SoftConnectionCircuitInfo +{ +public: + typedef std::list cluster_list; + typedef cluster_list::const_iterator cluster_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 cluster info object + */ + SoftConnectionClusterInfo &make_cluster (); + + /** + * @brief Adds information about a pin + * + * @param pin The pin + * @param dir The nature of connections from the pin: 0 if no soft connections / both directions, +1 to "up only" and -1 for "down only" + * @param sc_cluster_info The soft-connected net cluster info object + */ + void add_pin_info (const db::Pin *pin, int dir, SoftConnectionClusterInfo *sc_cluster_info); + + /** + * @brief Gets the direction attribute of the pin + */ + int direction_per_pin (const db::Pin *pin) const; + + /** + * @brief Gets the soft-connected net cluster info object the pin connects to + */ + const SoftConnectionClusterInfo *get_cluster_info_per_pin (const db::Pin *pin) const; + + /** + * @brief List of per-circui info objects, begin iterator + */ + cluster_list_iterator begin () const + { + return m_cluster_info.begin (); + } + + /** + * @brief List of per-circui info objects, end iterator + */ + cluster_list_iterator end () const + { + return m_cluster_info.end (); + } + +private: + const db::Circuit *mp_circuit; + cluster_list m_cluster_info; + 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 net clusters + */ + void build (const db::Netlist &netlist, const db::hier_clusters &net_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 For debugging + */ + void print_errors (const db::Netlist &netlist); + +private: + std::map m_scc_per_circuit; + + /** + * @brief Builds the per-circuit cluster information + * + * First of all, this method creates a SoftConnectionCircuitInfo object for the circuit. + * + * Inside this per-circuit object, it will create a number of SoftConnectionClusterInfo 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_clusters_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); +}; + +} + +#endif From b10cda574cd88e7d1c97fa54b162efcfa510c6f5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 17 Mar 2024 17:39:39 +0100 Subject: [PATCH 20/42] Reporting --- src/db/db/dbLayoutToNetlist.cc | 34 +++----- src/db/db/dbLayoutToNetlistSoftConnections.cc | 85 +++++++++++++++++-- src/db/db/dbLayoutToNetlistSoftConnections.h | 10 ++- 3 files changed, 99 insertions(+), 30 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 0b2ab5c55..74aa559cb 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -545,7 +545,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 (); @@ -553,15 +553,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; } @@ -587,12 +585,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"); @@ -600,12 +598,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"); @@ -626,7 +624,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"); @@ -634,7 +632,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"); @@ -691,11 +689,7 @@ void LayoutToNetlist::do_soft_connections () { SoftConnectionInfo sc_info; sc_info.build (*netlist (), net_clusters ()); - - // @@@ - sc_info.print_errors (*netlist ()); - // @@@ - + sc_info.report (*this); sc_info.join_soft_connections (*netlist ()); } diff --git a/src/db/db/dbLayoutToNetlistSoftConnections.cc b/src/db/db/dbLayoutToNetlistSoftConnections.cc index 0380d4dca..7c3a071f9 100644 --- a/src/db/db/dbLayoutToNetlistSoftConnections.cc +++ b/src/db/db/dbLayoutToNetlistSoftConnections.cc @@ -24,6 +24,7 @@ #include "dbCommon.h" #include "dbLayoutToNetlistSoftConnections.h" #include "dbLayoutToNetlist.h" +#include "dbHierNetworkProcessor.h" #include "dbNetlist.h" namespace db @@ -166,9 +167,79 @@ void SoftConnectionInfo::join_soft_connections (db::Netlist &netlist) m_scc_per_circuit.clear (); } -void SoftConnectionInfo::print_errors (const db::Netlist &netlist) +db::DPolygon +SoftConnectionInfo::representative_polygon (const db::Net *net, const db::LayoutToNetlist &l2n, const db::CplxTrans &trans) { - for (auto c = netlist.begin_bottom_up (); c != netlist.end_bottom_up (); ++c) { + 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 SoftConnectionClusterInfo &cluster_info, db::LayoutToNetlist &l2n, const std::string &path, const db::DCplxTrans &trans, const std::string &top_cell, int &index) +{ + for (auto cc = cluster_info.begin_clusters (); cc != cluster_info.end_clusters (); ++cc) { + + const db::Net *net = circuit->net_by_cluster_id (cc->first); + + if (cc->second < 0 && ! net->is_floating ()) { + + std::string msg = tl::sprintf (tl::to_string (tr ("Partial net #%d: %s - %s")), ++index, path, net->expanded_name ()); + + db::LogEntryData entry (l2n.top_level_mode () ? db::Error : db::Warning, 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 SoftConnectionClusterInfo *scci = sci.get_cluster_info_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); + + } + + } +} + +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 ()) { @@ -189,12 +260,10 @@ void SoftConnectionInfo::print_errors (const db::Netlist &netlist) first = false; } - tl::info << " Partial nets on soft-connect cluster:"; - for (auto cc = sc->begin_clusters (); cc != sc->end_clusters (); ++cc) { - if (cc->second < 0) { - tl::info << " " << c->net_by_cluster_id (cc->first)->expanded_name (); - } - } + l2n.log_entry (db::LogEntryData (l2n.top_level_mode () ? db::Error : db::Warning, c->name (), tl::to_string (tr ("Net with incomplete wiring (soft-connected partial nets)")))); + + int index = 0; + report_partial_nets (c.operator-> (), *sc, l2n, c->name (), db::DCplxTrans (), c->name (), index); } diff --git a/src/db/db/dbLayoutToNetlistSoftConnections.h b/src/db/db/dbLayoutToNetlistSoftConnections.h index 9c2bad69d..3ad5369a4 100644 --- a/src/db/db/dbLayoutToNetlistSoftConnections.h +++ b/src/db/db/dbLayoutToNetlistSoftConnections.h @@ -24,11 +24,13 @@ #define _HDR_dbLayoutToNetlistSoftConnections #include "dbCommon.h" +#include "dbPolygon.h" #include #include #include #include +#include namespace db { @@ -38,6 +40,7 @@ class Pin; class Circuit; class Netlist; class SubCircuit; +class LayoutToNetlist; template class hier_clusters; template class connected_clusters; @@ -219,9 +222,9 @@ public: void join_soft_connections (db::Netlist &netlist); /** - * @brief For debugging + * @brief Create log entries */ - void print_errors (const db::Netlist &netlist); + void report (db::LayoutToNetlist &l2n); private: std::map m_scc_per_circuit; @@ -262,6 +265,9 @@ private: * 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 SoftConnectionClusterInfo &cluster_info, LayoutToNetlist &l2n, const std::string &path, const db::DCplxTrans &trans, const std::string &top_cell, int &index); + db::DPolygon representative_polygon (const db::Net *net, const db::LayoutToNetlist &l2n, const db::CplxTrans &trans); }; } From 8984531a8c5dfeb7ef553f90f9f1da9f19ba71d1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 17 Mar 2024 18:36:49 +0100 Subject: [PATCH 21/42] Small enhancements of netlist browser: multiple items can be selected in log view (for manyfold geometries), highlights are cleared on tab change --- src/layui/layui/NetlistBrowserPage.ui | 5 ++++- src/layui/layui/layNetlistBrowserPage.cc | 9 +++++++++ src/layui/layui/layNetlistBrowserPage.h | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) 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/layNetlistBrowserPage.cc b/src/layui/layui/layNetlistBrowserPage.cc index 66cbdec49..9a6d5dc7b 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) { 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); From c04d468c55913ffd0c32a3834580a3bd62aa9f6e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 17 Mar 2024 20:40:11 +0100 Subject: [PATCH 22/42] Providing log entries with indents (for details) --- src/db/db/dbLog.cc | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) 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 (); From 6f48893cdb0caa7824dd727e5bea8e9237e758bc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 17 Mar 2024 20:40:44 +0100 Subject: [PATCH 23/42] Enabling special mode (hidden) for generating diodes for soft connections --- src/drc/drc/built-in-macros/_drc_engine.rb | 4 +++- src/drc/drc/built-in-macros/_drc_netter.rb | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 68a64d524..9bc76081e 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -2352,8 +2352,10 @@ CODE 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 0f977103d..627dd85a9 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -753,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 From 203a62f0afa4bab7b5c2bdaadb8a1714078750a4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 17 Mar 2024 20:41:45 +0100 Subject: [PATCH 24/42] Place diodes in debug mode for soft connections, but still generate log entries --- src/db/db/dbLayoutToNetlist.cc | 15 ++++++++------- src/db/db/dbLayoutToNetlist.h | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 74aa559cb..9a8fc5bbc 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -430,11 +430,7 @@ void LayoutToNetlist::extract_netlist () tl_assert (check_many_pins (mp_netlist.get ())); // @@@ // treat soft connections - if (m_make_soft_connection_diodes) { - do_make_soft_connection_diodes (); - } else { - do_soft_connections (); - } + do_soft_connections (); tl_assert (check_many_pins (mp_netlist.get ())); // @@@ // implement the "join_nets" (aka "must connect") feature @@ -650,7 +646,7 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N } } -void LayoutToNetlist::do_make_soft_connection_diodes () +void LayoutToNetlist::place_soft_connection_diodes () { db::DeviceClassDiode *soft_diode = 0; @@ -690,7 +686,12 @@ void LayoutToNetlist::do_soft_connections () SoftConnectionInfo sc_info; sc_info.build (*netlist (), net_clusters ()); sc_info.report (*this); - sc_info.join_soft_connections (*netlist ()); + + if (m_make_soft_connection_diodes) { + place_soft_connection_diodes (); + } else { + sc_info.join_soft_connections (*netlist ()); + } } void LayoutToNetlist::do_join_nets () diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 075804df1..812ee5d7b 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -1113,7 +1113,7 @@ private: 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 do_make_soft_connection_diodes (); + 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); From 3011e77439ee5e98090995fb1fbfec23551d7fdd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 17 Mar 2024 20:42:30 +0100 Subject: [PATCH 25/42] Need to differentiate between non-connected pins and pins with up+down connection - introducing a pin direction mode --- src/db/db/dbLayoutToNetlistSoftConnections.cc | 49 +++++----- src/db/db/dbLayoutToNetlistSoftConnections.h | 90 +++++++++++++++++-- 2 files changed, 107 insertions(+), 32 deletions(-) diff --git a/src/db/db/dbLayoutToNetlistSoftConnections.cc b/src/db/db/dbLayoutToNetlistSoftConnections.cc index 7c3a071f9..3ecaf4995 100644 --- a/src/db/db/dbLayoutToNetlistSoftConnections.cc +++ b/src/db/db/dbLayoutToNetlistSoftConnections.cc @@ -39,14 +39,14 @@ SoftConnectionClusterInfo::SoftConnectionClusterInfo () // .. nothing yet .. } -void SoftConnectionClusterInfo::add (const db::Net *net, int dir, const db::Pin *pin, size_t partial_net_count) +void SoftConnectionClusterInfo::add (const db::Net *net, SoftConnectionPinDir dir, const db::Pin *pin, size_t partial_net_count) { // limiting the partial net count to 1 means we report errors only once in the // hierarchy, not on every level m_partial_net_count += std::min (size_t (1), partial_net_count); // this is where we make the decision about the partial nets ... - if (! pin && dir < 0) { + if (! pin && dir == SoftConnectionPinDir::down ()) { m_partial_net_count += 1; } @@ -72,21 +72,21 @@ SoftConnectionClusterInfo &SoftConnectionCircuitInfo::make_cluster () return m_cluster_info.back (); } -void SoftConnectionCircuitInfo::add_pin_info (const db::Pin *pin, int dir, SoftConnectionClusterInfo *sc_cluster_info) +void SoftConnectionCircuitInfo::add_pin_info (const db::Pin *pin, SoftConnectionPinDir dir, SoftConnectionClusterInfo *sc_cluster_info) { if (pin) { m_pin_info.insert (std::make_pair (pin->id (), std::make_pair (dir, sc_cluster_info))); } } -int SoftConnectionCircuitInfo::direction_per_pin (const db::Pin *pin) const +SoftConnectionPinDir SoftConnectionCircuitInfo::direction_per_pin (const db::Pin *pin) const { if (! pin) { - return 0; + return SoftConnectionPinDir (); } auto p = m_pin_info.find (pin->id ()); - return p != m_pin_info.end () ? p->second.first : 0; + return p != m_pin_info.end () ? p->second.first : SoftConnectionPinDir (); } const SoftConnectionClusterInfo *SoftConnectionCircuitInfo::get_cluster_info_per_pin (const db::Pin *pin) const @@ -188,17 +188,21 @@ SoftConnectionInfo::representative_polygon (const db::Net *net, const db::Layout return db::DPolygon (bbox); } -void SoftConnectionInfo::report_partial_nets (const db::Circuit *circuit, const SoftConnectionClusterInfo &cluster_info, db::LayoutToNetlist &l2n, const std::string &path, const db::DCplxTrans &trans, const std::string &top_cell, int &index) +void SoftConnectionInfo::report_partial_nets (const db::Circuit *circuit, const SoftConnectionClusterInfo &cluster_info, db::LayoutToNetlist &l2n, const std::string &path, const db::DCplxTrans &trans, const std::string &top_cell, int &index, std::set &seen) { for (auto cc = cluster_info.begin_clusters (); cc != cluster_info.end_clusters (); ++cc) { const db::Net *net = circuit->net_by_cluster_id (cc->first); - if (cc->second < 0 && ! net->is_floating ()) { + if (! seen.insert (net).second) { + continue; + } - std::string msg = tl::sprintf (tl::to_string (tr ("Partial net #%d: %s - %s")), ++index, path, net->expanded_name ()); + if (cc->second == SoftConnectionPinDir::down () && ! net->is_floating () && net->begin_pins () == net->end_pins ()) { - db::LogEntryData entry (l2n.top_level_mode () ? db::Error : db::Warning, top_cell, msg); + 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); @@ -225,7 +229,7 @@ void SoftConnectionInfo::report_partial_nets (const db::Circuit *circuit, const 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); + report_partial_nets (circuit_ref, *scci, l2n, p, trans * subcircuit->trans (), top_cell, index, seen); } @@ -248,22 +252,19 @@ void SoftConnectionInfo::report (db::LayoutToNetlist &l2n) const SoftConnectionCircuitInfo &sc_info = scc->second; - bool first = true; for (auto sc = sc_info.begin (); sc != sc_info.end (); ++sc) { if (sc->partial_net_count () < 2) { continue; } - if (first) { - tl::info << "Circuit " << c->name () << ":"; - first = false; - } - - l2n.log_entry (db::LogEntryData (l2n.top_level_mode () ? db::Error : db::Warning, c->name (), tl::to_string (tr ("Net with incomplete wiring (soft-connected partial nets)")))); + 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; - report_partial_nets (c.operator-> (), *sc, l2n, c->name (), db::DCplxTrans (), c->name (), index); + std::set seen; + report_partial_nets (c.operator-> (), *sc, l2n, c->name (), db::DCplxTrans (), c->name (), index, seen); } @@ -303,14 +304,14 @@ void SoftConnectionInfo::build_clusters_for_circuit (const db::Circuit *circuit, // 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: - int dir = 0; + 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 ? 1 : -1; + dir |= up ? SoftConnectionPinDir::up () : SoftConnectionPinDir::down (); } for (auto i = next.begin (); i != next.end (); ++i) { if (seen.insert (*i).second) { @@ -362,15 +363,15 @@ void SoftConnectionInfo::build_clusters_for_circuit (const db::Circuit *circuit, bool SoftConnectionInfo::net_has_up_or_down_subcircuit_connections (const db::Net *net, bool up) { - int look_for_dir = up ? 1 : -1; + 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 ()) { - int dir = scc_ref->second.direction_per_pin (pin); - if (dir == look_for_dir) { + SoftConnectionPinDir dir = scc_ref->second.direction_per_pin (pin); + if (dir & look_for_dir) { return true; } } diff --git a/src/db/db/dbLayoutToNetlistSoftConnections.h b/src/db/db/dbLayoutToNetlistSoftConnections.h index 3ad5369a4..a13630465 100644 --- a/src/db/db/dbLayoutToNetlistSoftConnections.h +++ b/src/db/db/dbLayoutToNetlistSoftConnections.h @@ -46,6 +46,80 @@ 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 cluster * @@ -58,7 +132,7 @@ class DB_PUBLIC SoftConnectionClusterInfo public: typedef std::set pin_set; typedef pin_set::const_iterator pin_iterator; - typedef std::map dir_map; + typedef std::map dir_map; typedef dir_map::const_iterator dir_map_iterator; SoftConnectionClusterInfo (); @@ -67,11 +141,11 @@ public: * @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 (0: no soft connection or both directions, +1: up-only, -1: down-only) + * @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, int dir, const db::Pin *pin, size_t partial_net_count); + void add (const db::Net *net, SoftConnectionPinDir dir, const db::Pin *pin, size_t partial_net_count); /** * @brief Gets the partial net count @@ -163,15 +237,15 @@ public: * @brief Adds information about a pin * * @param pin The pin - * @param dir The nature of connections from the pin: 0 if no soft connections / both directions, +1 to "up only" and -1 for "down only" + * @param dir The direction of connections from the pin * @param sc_cluster_info The soft-connected net cluster info object */ - void add_pin_info (const db::Pin *pin, int dir, SoftConnectionClusterInfo *sc_cluster_info); + void add_pin_info (const db::Pin *pin, SoftConnectionPinDir dir, SoftConnectionClusterInfo *sc_cluster_info); /** * @brief Gets the direction attribute of the pin */ - int direction_per_pin (const db::Pin *pin) const; + SoftConnectionPinDir direction_per_pin (const db::Pin *pin) const; /** * @brief Gets the soft-connected net cluster info object the pin connects to @@ -197,7 +271,7 @@ public: private: const db::Circuit *mp_circuit; cluster_list m_cluster_info; - std::map > m_pin_info; + std::map > m_pin_info; }; /** @@ -266,7 +340,7 @@ private: */ std::set net_connections_through_subcircuits (const db::Net *net, size_t &partial_net_count); - void report_partial_nets (const db::Circuit *circuit, const SoftConnectionClusterInfo &cluster_info, LayoutToNetlist &l2n, const std::string &path, const db::DCplxTrans &trans, const std::string &top_cell, int &index); + void report_partial_nets (const db::Circuit *circuit, const SoftConnectionClusterInfo &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); }; From 92a9b491394bc6defb30c75bab52f803576a5101 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 17 Mar 2024 23:08:20 +0100 Subject: [PATCH 26/42] Added images for doc --- src/doc/doc/manual/soft_connections.png | Bin 0 -> 29622 bytes src/doc/doc/manual/soft_connections2.png | Bin 0 -> 34316 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/doc/doc/manual/soft_connections.png create mode 100644 src/doc/doc/manual/soft_connections2.png diff --git a/src/doc/doc/manual/soft_connections.png b/src/doc/doc/manual/soft_connections.png new file mode 100644 index 0000000000000000000000000000000000000000..a13e6f93b6184e3667d4b9a56a7b041bc0c38f7a GIT binary patch literal 29622 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 literal 0 HcmV?d00001 diff --git a/src/doc/doc/manual/soft_connections2.png b/src/doc/doc/manual/soft_connections2.png new file mode 100644 index 0000000000000000000000000000000000000000..cf0ff5cd01f7e5bf0e9175a3da26546ab11b21fb GIT binary patch literal 34316 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 B8KwXL literal 0 HcmV?d00001 From a04bcbc9a373ebf0740cdde994e7d75ccfcabe4d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 19 Mar 2024 20:41:42 +0100 Subject: [PATCH 27/42] Some refactoring (renaming) --- src/db/db/dbLayoutToNetlistSoftConnections.cc | 58 ++++++++-------- src/db/db/dbLayoutToNetlistSoftConnections.h | 68 ++++++++++--------- 2 files changed, 65 insertions(+), 61 deletions(-) diff --git a/src/db/db/dbLayoutToNetlistSoftConnections.cc b/src/db/db/dbLayoutToNetlistSoftConnections.cc index 3ecaf4995..b915f4c54 100644 --- a/src/db/db/dbLayoutToNetlistSoftConnections.cc +++ b/src/db/db/dbLayoutToNetlistSoftConnections.cc @@ -31,15 +31,15 @@ namespace db { // ------------------------------------------------------------------------------- -// SoftConnectionClusterInfo implementation +// SoftConnectionNetGraph implementation -SoftConnectionClusterInfo::SoftConnectionClusterInfo () +SoftConnectionNetGraph::SoftConnectionNetGraph () : m_partial_net_count (0) { // .. nothing yet .. } -void SoftConnectionClusterInfo::add (const db::Net *net, SoftConnectionPinDir dir, const db::Pin *pin, size_t partial_net_count) +void SoftConnectionNetGraph::add (const db::Net *net, SoftConnectionPinDir dir, const db::Pin *pin, size_t partial_net_count) { // limiting the partial net count to 1 means we report errors only once in the // hierarchy, not on every level @@ -66,16 +66,16 @@ SoftConnectionCircuitInfo::SoftConnectionCircuitInfo (const db::Circuit *circuit // .. nothing yet .. } -SoftConnectionClusterInfo &SoftConnectionCircuitInfo::make_cluster () +SoftConnectionNetGraph &SoftConnectionCircuitInfo::make_net_graph () { - m_cluster_info.push_back (SoftConnectionClusterInfo ()); - return m_cluster_info.back (); + m_net_graphs.push_back (SoftConnectionNetGraph ()); + return m_net_graphs.back (); } -void SoftConnectionCircuitInfo::add_pin_info (const db::Pin *pin, SoftConnectionPinDir dir, SoftConnectionClusterInfo *sc_cluster_info) +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, sc_cluster_info))); + m_pin_info.insert (std::make_pair (pin->id (), std::make_pair (dir, graph_info))); } } @@ -89,7 +89,7 @@ SoftConnectionPinDir SoftConnectionCircuitInfo::direction_per_pin (const db::Pin return p != m_pin_info.end () ? p->second.first : SoftConnectionPinDir (); } -const SoftConnectionClusterInfo *SoftConnectionCircuitInfo::get_cluster_info_per_pin (const db::Pin *pin) const +const SoftConnectionNetGraph *SoftConnectionCircuitInfo::get_net_graph_per_pin (const db::Pin *pin) const { if (! pin) { return 0; @@ -107,25 +107,25 @@ SoftConnectionInfo::SoftConnectionInfo () // .. nothing yet .. } -void SoftConnectionInfo::build (const db::Netlist &netlist, const db::hier_clusters &net_clusters) +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_clusters_for_circuit (c.operator-> (), net_clusters.clusters_per_cell (c->cell_index ())); + 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 clusters .."; + tl::info << "Joining soft-connected net graphs .."; } - size_t nclusters_tot = 0; + 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 nclusters = 0; + size_t nnet_graphs = 0; size_t npartial = 0; auto scc = m_scc_per_circuit.find (c.operator-> ()); @@ -141,7 +141,7 @@ void SoftConnectionInfo::join_soft_connections (db::Netlist &netlist) if (cc != sc->end_clusters ()) { db::Net *net0 = c->net_by_cluster_id (cc->first); tl_assert (net0 != 0); - ++nclusters; + ++nnet_graphs; while (++cc != sc->end_clusters ()) { // TODO: logging? c->join_nets (net0, c->net_by_cluster_id (cc->first)); @@ -151,17 +151,17 @@ void SoftConnectionInfo::join_soft_connections (db::Netlist &netlist) } - nclusters_tot += nclusters; + nnet_graphs_tot += nnet_graphs; npartial_tot += npartial; - if (nclusters > 0 && tl::verbosity () >= 30) { - tl::info << "Circuit " << c->name () << ": joined " << nclusters << " soft-connected net clusters with " << npartial << " partial nets."; + 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 " << nclusters_tot << " soft-connected net clusters with " << npartial_tot << " partial nets in total."; + tl::info << "Joined " << nnet_graphs_tot << " soft-connected net clusters with " << npartial_tot << " partial nets in total."; } m_scc_per_circuit.clear (); @@ -188,9 +188,9 @@ SoftConnectionInfo::representative_polygon (const db::Net *net, const db::Layout return db::DPolygon (bbox); } -void SoftConnectionInfo::report_partial_nets (const db::Circuit *circuit, const SoftConnectionClusterInfo &cluster_info, db::LayoutToNetlist &l2n, const std::string &path, const db::DCplxTrans &trans, const std::string &top_cell, int &index, std::set &seen) +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 = cluster_info.begin_clusters (); cc != cluster_info.end_clusters (); ++cc) { + for (auto cc = net_graph.begin_clusters (); cc != net_graph.end_clusters (); ++cc) { const db::Net *net = circuit->net_by_cluster_id (cc->first); @@ -221,7 +221,7 @@ void SoftConnectionInfo::report_partial_nets (const db::Circuit *circuit, const const SoftConnectionCircuitInfo &sci = scc->second; - const SoftConnectionClusterInfo *scci = sci.get_cluster_info_per_pin (sc->pin ()); + const SoftConnectionNetGraph *scci = sci.get_net_graph_per_pin (sc->pin ()); if (! scci || scci->partial_net_count () == 0) { continue; } @@ -271,7 +271,7 @@ void SoftConnectionInfo::report (db::LayoutToNetlist &l2n) } } -void SoftConnectionInfo::build_clusters_for_circuit (const db::Circuit *circuit, const db::connected_clusters &shape_clusters) +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; @@ -288,7 +288,7 @@ void SoftConnectionInfo::build_clusters_for_circuit (const db::Circuit *circuit, connected.insert (c->id ()); seen.insert (c->id ()); - SoftConnectionClusterInfo *sc_cluster_info = 0; + SoftConnectionNetGraph *sc_net_graph = 0; while (! connected.empty ()) { @@ -340,16 +340,16 @@ void SoftConnectionInfo::build_clusters_for_circuit (const db::Circuit *circuit, pin = net->begin_pins ()->pin (); } - if (! sc_cluster_info) { - sc_cluster_info = &sc_circuit_info.make_cluster (); + 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_cluster_info->add (net, dir, pin, sc_partial_net_count); + sc_net_graph->add (net, dir, pin, sc_partial_net_count); } - sc_circuit_info.add_pin_info (pin, dir, sc_cluster_info); + sc_circuit_info.add_pin_info (pin, dir, sc_net_graph); } @@ -387,7 +387,7 @@ void SoftConnectionInfo::get_net_connections_through_subcircuit (const db::SubCi const SoftConnectionCircuitInfo &sci = scc->second; - const SoftConnectionClusterInfo *scci = sci.get_cluster_info_per_pin (pin); + const SoftConnectionNetGraph *scci = sci.get_net_graph_per_pin (pin); if (scci) { partial_net_count += scci->partial_net_count (); diff --git a/src/db/db/dbLayoutToNetlistSoftConnections.h b/src/db/db/dbLayoutToNetlistSoftConnections.h index a13630465..37e96d40d 100644 --- a/src/db/db/dbLayoutToNetlistSoftConnections.h +++ b/src/db/db/dbLayoutToNetlistSoftConnections.h @@ -121,13 +121,17 @@ private: }; /** - * @brief Describes a soft-connected cluster + * @brief Describes a soft-connected net graph * - * Such a cluster is a collection of nets/shape clusters that are connected via + * 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. + * 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 SoftConnectionClusterInfo +class DB_PUBLIC SoftConnectionNetGraph { public: typedef std::set pin_set; @@ -135,7 +139,7 @@ public: typedef std::map dir_map; typedef dir_map::const_iterator dir_map_iterator; - SoftConnectionClusterInfo (); + SoftConnectionNetGraph (); /** * @brief Enters information about a specific net @@ -164,9 +168,9 @@ public: } /** - * @brief Gets the pins on the cluster (begin iterator) + * @brief Gets the outside pins on the net graph (begin iterator) * - * The iterator delivers Pin IDs of pins leading outside the circuit this cluster lives in. + * The iterator delivers Pin IDs of pins leading outside the circuit this graph lives in. */ pin_iterator begin_pins () const { @@ -174,7 +178,7 @@ public: } /** - * @brief Gets the pins on the cluster (end iterator) + * @brief Gets the pins on the net graph (end iterator) */ pin_iterator end_pins () const { @@ -206,14 +210,14 @@ private: /** * @brief Provides temporary soft connection information for a circuit * - * Soft connection information is the soft-connected-clusters that are formed inside - * the circuit and how these clusters connect to pins. + * 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 cluster_list; - typedef cluster_list::const_iterator cluster_list_iterator; + typedef std::list net_graph_list; + typedef net_graph_list::const_iterator net_graph_list_iterator; /** * @brief Constructor @@ -229,18 +233,18 @@ public: } /** - * @brief Creates a new cluster info object + * @brief Creates a new graph info object */ - SoftConnectionClusterInfo &make_cluster (); + SoftConnectionNetGraph &make_net_graph (); /** * @brief Adds information about a pin * * @param pin The pin * @param dir The direction of connections from the pin - * @param sc_cluster_info The soft-connected net cluster info object + * @param graph_info The soft-connected net graph info object */ - void add_pin_info (const db::Pin *pin, SoftConnectionPinDir dir, SoftConnectionClusterInfo *sc_cluster_info); + void add_pin_info (const db::Pin *pin, SoftConnectionPinDir dir, SoftConnectionNetGraph *graph_info); /** * @brief Gets the direction attribute of the pin @@ -248,30 +252,30 @@ public: SoftConnectionPinDir direction_per_pin (const db::Pin *pin) const; /** - * @brief Gets the soft-connected net cluster info object the pin connects to + * @brief Gets the soft-connected net graph object the pin connects to */ - const SoftConnectionClusterInfo *get_cluster_info_per_pin (const db::Pin *pin) const; + const SoftConnectionNetGraph *get_net_graph_per_pin (const db::Pin *pin) const; /** - * @brief List of per-circui info objects, begin iterator + * @brief List of per-circuit net graph objects, begin iterator */ - cluster_list_iterator begin () const + net_graph_list_iterator begin () const { - return m_cluster_info.begin (); + return m_net_graphs.begin (); } /** - * @brief List of per-circui info objects, end iterator + * @brief List of per-circuit net graph objects, end iterator */ - cluster_list_iterator end () const + net_graph_list_iterator end () const { - return m_cluster_info.end (); + return m_net_graphs.end (); } private: const db::Circuit *mp_circuit; - cluster_list m_cluster_info; - std::map > m_pin_info; + net_graph_list m_net_graphs; + std::map > m_pin_info; }; /** @@ -283,9 +287,9 @@ public: SoftConnectionInfo (); /** - * @brief Builds the soft connection information for the given netlist and net clusters + * @brief Builds the soft connection information for the given netlist and shape clusters */ - void build (const db::Netlist &netlist, const db::hier_clusters &net_clusters); + void build (const db::Netlist &netlist, const db::hier_clusters &shape_clusters); /** * @brief Joins nets connected by soft connections @@ -304,16 +308,16 @@ private: std::map m_scc_per_circuit; /** - * @brief Builds the per-circuit cluster information + * @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 SoftConnectionClusterInfo objects - each one + * 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_clusters_for_circuit (const db::Circuit *circuit, const db::connected_clusters &shape_clusters); + 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 @@ -340,7 +344,7 @@ private: */ std::set net_connections_through_subcircuits (const db::Net *net, size_t &partial_net_count); - void report_partial_nets (const db::Circuit *circuit, const SoftConnectionClusterInfo &cluster_info, LayoutToNetlist &l2n, const std::string &path, const db::DCplxTrans &trans, const std::string &top_cell, int &index, std::set &seen); + 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); }; From 33d8befa1ba5da6d6a9cae8390ae03e8987e2c51 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 19 Mar 2024 21:25:06 +0100 Subject: [PATCH 28/42] [consider merging] avoid a segfault when loading a l2n db with '-mn' into KLayout --- src/layui/layui/layNetlistBrowserPage.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layui/layui/layNetlistBrowserPage.cc b/src/layui/layui/layNetlistBrowserPage.cc index 9a6d5dc7b..a6faa140e 100644 --- a/src/layui/layui/layNetlistBrowserPage.cc +++ b/src/layui/layui/layNetlistBrowserPage.cc @@ -1175,7 +1175,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); From a0122f49d9213033d3e1c62e3afe739c35af7ac6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 19 Mar 2024 23:25:50 +0100 Subject: [PATCH 29/42] Netlist browser: log tab will be visible also for L2N database now --- src/layui/layui/layNetlistBrowserPage.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/layui/layui/layNetlistBrowserPage.cc b/src/layui/layui/layNetlistBrowserPage.cc index a6faa140e..086dc39bb 100644 --- a/src/layui/layui/layNetlistBrowserPage.cc +++ b/src/layui/layui/layNetlistBrowserPage.cc @@ -1081,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) { From 400becb6b6fa8ce0231a50172afd0cc65737499e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 20 Mar 2024 02:03:53 +0100 Subject: [PATCH 30/42] Bugfix: proper handling of soft-connected instance connectors --- src/db/db/dbHierNetworkProcessor.cc | 56 ++++++++++++++-------------- src/lvs/unit_tests/lvsSimpleTests.cc | 6 +++ 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 6ed288b23..d05560fab 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -858,19 +858,20 @@ 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); - - // NOTE: we cannot really delete a cluster as this would shift the indexes. So - // we just clear them. - with->clear (); - // take care of soft connections std::set conn_down = downward_soft_connections (with_id); @@ -1821,22 +1822,6 @@ public: } - for (typename std::list >::const_iterator sc = m_cm2join_sets.begin (); sc != m_cm2join_sets.end (); ++sc) { - - if (sc->empty ()) { - // dropped ones are empty - continue; - } - - typename std::set::const_iterator c = sc->begin (); - typename std::set::const_iterator cc = c; - for (++cc; cc != sc->end (); ++cc) { - mp_cell_clusters->join_cluster_with (*c, *cc); - m_soft_connections.erase (std::make_pair (*c < *cc ? *c : *cc, *c < *cc ? *cc : *c)); - } - - } - for (auto sc = m_soft_connections.begin (); sc != m_soft_connections.end (); ++sc) { if (sc->second == 0) { @@ -1851,6 +1836,23 @@ public: } } + + for (typename std::list >::const_iterator sc = m_cm2join_sets.begin (); sc != m_cm2join_sets.end (); ++sc) { + + if (sc->empty ()) { + // dropped ones are empty + continue; + } + + typename std::set::const_iterator c = sc->begin (); + typename std::set::const_iterator cc = c; + for (++cc; cc != sc->end (); ++cc) { + mp_cell_clusters->join_cluster_with (*c, *cc); + // @@@ m_soft_connections.erase (std::make_pair (*c < *cc ? *c : *cc, *c < *cc ? *cc : *c)); + } + + } + } // needs explicit implementation because we have two base classes: diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index a5603bd3f..fe7f97127 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -301,3 +301,9 @@ TEST(40_DeviceExtractorErrors) run_test (_this, "custom_resistors", "custom_resistors.gds", true, false /*no LVS*/); } +// Basic soft connection +TEST(50_SoftConnection) +{ + run_test (_this, "soft_connect1", "soft_connect1.gds", true, false /*no LVS*/); +} + From 962e212e19c4128b7b48820cf0acab4dcec72308 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 20 Mar 2024 23:23:11 +0100 Subject: [PATCH 31/42] regression test for soft connect feature --- testdata/lvs/soft_connect1.cir | 13 ++ testdata/lvs/soft_connect1.gds | Bin 0 -> 3238 bytes testdata/lvs/soft_connect1.l2n | 217 +++++++++++++++++++++++++++++++++ testdata/lvs/soft_connect1.lvs | 92 ++++++++++++++ 4 files changed, 322 insertions(+) create mode 100644 testdata/lvs/soft_connect1.cir create mode 100644 testdata/lvs/soft_connect1.gds create mode 100644 testdata/lvs/soft_connect1.l2n create mode 100644 testdata/lvs/soft_connect1.lvs 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 0000000000000000000000000000000000000000..f2ff6e943c48ca8cad01df26ffa36c05a0329a85 GIT binary patch literal 3238 zcmbW3Jxmlq6vyB0-R&p6@`5F!6ntMght395WnI+2lL|(-u zm%J92h(_8nK~P@|g2phD$oWQ;SypPCN!j+u&&K_Ri}pSIfx!m&J)*`NducvqGS5E7 z9b-SD#%aEpkG_}S>Po7i9l3;rQMl@1ozD!T%HLYGV?1@{oz0yeOmQDK$TQ z2oA=K6Ww`8P4}}^(&kyGh-^?4n(##+6 z#QYt5e4@OMG0?B&tBkMIc;2_>pO5?hkw0gA%@fTVrN$HabH>*_*St|`Jdr;MA8Y@& z{E_-|;#Z|;#(KrKoT*6a&9sD+LyA5+|ze;|;H&?BO=UIAh)GGI$<*#!-{leZj zpOVkMux(c0^?MU`U%c~VUmrV*)uQjCurFQ%&p8*}jac6zdaVfY4MjbevQp!qnHTal Go26gk>B;~A literal 0 HcmV?d00001 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 + + From 265b5680de2a9beac5bc973aec2d2da1aba02f4f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 20 Mar 2024 23:29:25 +0100 Subject: [PATCH 32/42] Added soft-connect test for valid layout --- src/lvs/unit_tests/lvsSimpleTests.cc | 6 + testdata/lvs/soft_connect2.cir | 13 ++ testdata/lvs/soft_connect2.gds | Bin 0 -> 3366 bytes testdata/lvs/soft_connect2.l2n | 213 +++++++++++++++++++++++++++ testdata/lvs/soft_connect2.lvs | 92 ++++++++++++ 5 files changed, 324 insertions(+) create mode 100644 testdata/lvs/soft_connect2.cir create mode 100644 testdata/lvs/soft_connect2.gds create mode 100644 testdata/lvs/soft_connect2.l2n create mode 100644 testdata/lvs/soft_connect2.lvs diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index fe7f97127..f5eb6e9f8 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -307,3 +307,9 @@ TEST(50_SoftConnection) run_test (_this, "soft_connect1", "soft_connect1.gds", true, false /*no LVS*/); } +// No errors +TEST(51_SoftConnection) +{ + run_test (_this, "soft_connect2", "soft_connect2.gds", true, false /*no LVS*/); +} + 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 0000000000000000000000000000000000000000..a223df0984e5cc8d011b00c35f4f6ec0e5ea2d35 GIT binary patch literal 3366 zcmb7{O-L0{6vxlZd(OM>`7AX*%SB2-i?9}DMj#mmNzs=N5oy_?RkUbnZFCd3Xerdf zO(wJp0wGr+5w2YXMYw3;QlgdEoBrpXInBMJZ?whhC+FVZ+^=)bOcYTVPElVFmbR!s z9n?+7X~6xPOc>o7zedziXc`%Pap~sgx7(M;R?aQHnyyh_Q)90ZMYa2*w^$_jr_<`{f_#>sp>+5+wW-`q_ z#vNimrN(Kxk&nIuaMp$K5?JT)g&J=>K6^aAFc@MFzl0jkj32q{|Feqm=i>O!Dm?6E zd`hWtrZ<1b$JpO(JIEeUIHuHi>X+sG3d#6UKcAvGTEM?M)X~Z$oa81GCwWpvEm3ZM zm?#>*J&Dt;5LMbWSE}+f`yhMv`?prM8?B6Y1T%jiJ5zotJ0sLM)AMy@HZ$W{I1zrc zSR_uEo+&lX^nBx<%+4u1e6{3i-UH)Hrh=KU5$2vLJrT4e^y4Hw)q) zd`D{I*us4)XgrK@c5yaf^(XfHOG=HK-F@bnRN#0R=Uf#(_$|Ir<4n)@DscRosJR9o ztoFfwm{Q}-wS2$4CVpjJe5J?8`G!Cym!PJm0CH z=<`JGDUReQ)$oh(Fb*c`*D3>U#M}W=X-UA&(8YQ#KRd{|F^~u@{{?)xATXu zwDU(iug)MhY@K0pec{|ua-ETOZ~ji5kUiHtIb{Cq@ik90Zo&s_6Hsqswy zWPGgg|MJJHGsrDlCzxCxcx)xt32FECPyD3L@Yr0ZxF6Rk_JtZx>(n9Y%$>XPE;jA* zUbb)I<`um9ugV9O^%Hri};&8zc;>#xc3DA@4)Ge@38E%PpsM 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 + + From 366249645a521d2fd59babed858322ad9e4a66af Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 20 Mar 2024 23:37:10 +0100 Subject: [PATCH 33/42] Another test --- src/lvs/unit_tests/lvsSimpleTests.cc | 6 + testdata/lvs/soft_connect3.cir | 21 +++ testdata/lvs/soft_connect3.gds | Bin 0 -> 3782 bytes testdata/lvs/soft_connect3.l2n | 257 +++++++++++++++++++++++++++ testdata/lvs/soft_connect3.lvs | 92 ++++++++++ 5 files changed, 376 insertions(+) create mode 100644 testdata/lvs/soft_connect3.cir create mode 100644 testdata/lvs/soft_connect3.gds create mode 100644 testdata/lvs/soft_connect3.l2n create mode 100644 testdata/lvs/soft_connect3.lvs diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index f5eb6e9f8..e1fb2b470 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -313,3 +313,9 @@ TEST(51_SoftConnection) run_test (_this, "soft_connect2", "soft_connect2.gds", true, false /*no LVS*/); } +// Simple hierarchy +TEST(52_SoftConnection) +{ + run_test (_this, "soft_connect3", "soft_connect3.gds", true, false /*no LVS*/); +} + 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 0000000000000000000000000000000000000000..62a10be1e7c4a7431b7d5d69533076538cad970b GIT binary patch literal 3782 zcmeH~J4_T&6o${tF0->QdB`(T2!X_cpoJpFgos%a2?S9#gb+&#OAAY^pfRDaB+-IK zB(XCwA(6@uW2h-iG={>0l7foBj{mu54l{Rf2Tbg=82-(F?{}Z)+ZNl0H~FqNF?N@zHPbLM`r_vJ=ePT}DjQcr@4AO>h^-!77f;*HSS+j{rdeg$Nh^sL(Jg|RsBT&UVQ$?o9KTj2#zA5*KN19od}4eeYTy&VsQRpFJjOjJeCfULs_N$lE#9&FC(8N4U-&A# zs`@$K;&Ty?v0lXZK8gk(aDJ3A-%!v@f-`=pnFyi6}YXdF)#GJoGs=j|@jnlfJgJG<5NqFZ# zcv1CPE#A7JgV#jGDtKtA8~l8v>KCi&e11##!m{wH>gTu8d72mA84zAoeT*r-Pl@qk zeml%d8xa%vwH=GM^1Sv`@`RQ1!+9Ag=ZTc3@u-75<$9c-%!{heYVlT{^WGW06mr-@ z!yl-B79Wluycs`uRWpA0X?c#=FnP|(`OUGT%6Ts3*7#$2Dr>HBa?bcU#cP~s+^DLb zh@Vru?zzT|s``oeN${BCpT&>lUwD@xw#Z-MHi*jiv3>)Qs_(B@>u0^SxNm-IG0$%; z=0(+K75*IO%!4Vp3;h#I&mRi5#|0na0jZY4~^%w#phatXJrmCaH7nI<%X~S@i31>HA=h{Vs|A`DG~>0x8PM($TIq1`+pKmI#@2j|{4Q^% zQo%0r|5WL<@h0Hf)6;fsyhD<0U)nS?@VdNFyDru|S=YzTVz#LJFszGZ;4wGE@AtL^ Y)LOC;$Ke literal 0 HcmV?d00001 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 + + From 917439b84e6692f1439f6a71213a16b2b9fd2280 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 21 Mar 2024 21:31:20 +0100 Subject: [PATCH 34/42] New testdata, bugfix: considering the case of partial net formation through soft, upward inter-instance connections. Enhanced reporting (report multiple partial nets of the same circuit at different instances) --- src/db/db/dbLayoutToNetlistSoftConnections.cc | 18 ++-- src/db/db/dbLayoutToNetlistSoftConnections.h | 2 +- testdata/lvs/soft_connect4.cir | 13 +++ testdata/lvs/soft_connect4.gds | Bin 0 -> 3118 bytes testdata/lvs/soft_connect4.lvs | 93 ++++++++++++++++++ 5 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 testdata/lvs/soft_connect4.cir create mode 100644 testdata/lvs/soft_connect4.gds create mode 100644 testdata/lvs/soft_connect4.lvs diff --git a/src/db/db/dbLayoutToNetlistSoftConnections.cc b/src/db/db/dbLayoutToNetlistSoftConnections.cc index b915f4c54..56db8ca9a 100644 --- a/src/db/db/dbLayoutToNetlistSoftConnections.cc +++ b/src/db/db/dbLayoutToNetlistSoftConnections.cc @@ -41,9 +41,7 @@ SoftConnectionNetGraph::SoftConnectionNetGraph () void SoftConnectionNetGraph::add (const db::Net *net, SoftConnectionPinDir dir, const db::Pin *pin, size_t partial_net_count) { - // limiting the partial net count to 1 means we report errors only once in the - // hierarchy, not on every level - m_partial_net_count += std::min (size_t (1), 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 ()) { @@ -188,13 +186,13 @@ SoftConnectionInfo::representative_polygon (const db::Net *net, const db::Layout 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) +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 (net).second) { + if (! seen.insert (std::make_pair (net, trans)).second) { continue; } @@ -263,7 +261,7 @@ void SoftConnectionInfo::report (db::LayoutToNetlist &l2n) l2n.log_entry (log_entry); int index = 0; - std::set seen; + std::set > seen; report_partial_nets (c.operator-> (), *sc, l2n, c->name (), db::DCplxTrans (), c->name (), index, seen); } @@ -390,7 +388,13 @@ void SoftConnectionInfo::get_net_connections_through_subcircuit (const db::SubCi const SoftConnectionNetGraph *scci = sci.get_net_graph_per_pin (pin); if (scci) { - partial_net_count += scci->partial_net_count (); + // 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 ()) { diff --git a/src/db/db/dbLayoutToNetlistSoftConnections.h b/src/db/db/dbLayoutToNetlistSoftConnections.h index 37e96d40d..dbc65698f 100644 --- a/src/db/db/dbLayoutToNetlistSoftConnections.h +++ b/src/db/db/dbLayoutToNetlistSoftConnections.h @@ -344,7 +344,7 @@ private: */ 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); + 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); }; 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 0000000000000000000000000000000000000000..5c19d896d18d3dd85e4250344df01e0db69138b3 GIT binary patch literal 3118 zcmb7`J7^S96o&tq-OSGJ&T2Fdldy=2fbMI>0Ju(Ys@RniD4ECnsn z7zH~)2$(7ak<=D~A{G{wDXhY}M1}PW=gxP}eV!Qv@clbzdVb|QN*KZb zA`ImJM(77uX3hhHrM{`@N2e~od$D!q!s_wmr?V|I`wF}uXw6&%${`RmeE0}<`2fcI zOK}{Z2Ew*7`o`#cz-aNXjq;wJejIlq^}QeJD=jW`{IZk(8BlN0C!;3)xr`RCw~GDh zhWfQ7^^F#o#3-%^cwIU zXS8^AqR6K^rM|bWzS81C#}A$S+`Gh&=!(xhR9bu|KOH~)Wc>7v$@m%X8o%e}2Pz?R z@VQ+dHNVl~mCzY~CGX3*v2*f|=jRXK&WW8Hqs6<<&mX?+xt$xM#k*@XF*+Te4`T!c2xPIdqEgn8`)=z!k{5bykQhnB+#8+BesD1^7b*9ETb94IYtsa%4 zJ-cQnJeW0tRw7AgmZPxmJT%Z&1 zx4^{QeLOT;T5Ww+LWK3V;Q78e%zT3^MNx+=a=>Z`Z`O%9TiGl z72jy_-u0DZ9pL!hD&sdiH4V-;rNzno`seDKcwDo~N{^f7H~4o-i<5{b(c)I0cfLQZ zYuEMu@D3z*DY*mnRbJO~ItINTS=;`>tc_PizW;Lf0~np-Wjv~sewxF4pQf}pnO`5r zuk2M{C?4OHzbSs&p7_113cq@isxro}2i|nl?sU)InXA=_bgsE?4Pf`OuDx(* z1Q_GH(kOe9wkKKnIU;MtS}^Q`z3lT4YQ;Igu9+nm4W`q&lRnFJ>3%jmVb9)hYgAwM c4a2p6&!?7@{MN-7zX6RFCzEmUjFijx1vZVr9{>OV literal 0 HcmV?d00001 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 + + From 10cf9c0bb66891e6fba938c00969427805665279 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 21 Mar 2024 22:46:05 +0100 Subject: [PATCH 35/42] More tests --- src/lvs/unit_tests/lvsSimpleTests.cc | 23 ++- testdata/lvs/soft_connect4.l2n | 192 +++++++++++++++++++++++++ testdata/lvs/soft_connect5.cir | 13 ++ testdata/lvs/soft_connect5.gds | Bin 0 -> 3210 bytes testdata/lvs/soft_connect5.l2n | 196 ++++++++++++++++++++++++++ testdata/lvs/soft_connect5.lvs | 93 +++++++++++++ testdata/lvs/soft_connect6.cir | 13 ++ testdata/lvs/soft_connect6.gds | Bin 0 -> 3710 bytes testdata/lvs/soft_connect6.l2n | 201 +++++++++++++++++++++++++++ testdata/lvs/soft_connect6.lvs | 93 +++++++++++++ 10 files changed, 821 insertions(+), 3 deletions(-) create mode 100644 testdata/lvs/soft_connect4.l2n create mode 100644 testdata/lvs/soft_connect5.cir create mode 100644 testdata/lvs/soft_connect5.gds create mode 100644 testdata/lvs/soft_connect5.l2n create mode 100644 testdata/lvs/soft_connect5.lvs create mode 100644 testdata/lvs/soft_connect6.cir create mode 100644 testdata/lvs/soft_connect6.gds create mode 100644 testdata/lvs/soft_connect6.l2n create mode 100644 testdata/lvs/soft_connect6.lvs diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index e1fb2b470..ac141d640 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -302,20 +302,37 @@ TEST(40_DeviceExtractorErrors) } // Basic soft connection -TEST(50_SoftConnection) +TEST(50_BasicSoftConnection) { run_test (_this, "soft_connect1", "soft_connect1.gds", true, false /*no LVS*/); } // No errors -TEST(51_SoftConnection) +TEST(51_SoftConnectionNoErrors) { run_test (_this, "soft_connect2", "soft_connect2.gds", true, false /*no LVS*/); } // Simple hierarchy -TEST(52_SoftConnection) +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/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_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 0000000000000000000000000000000000000000..a33882ebc1eb126bd2a0b4fd5fe716c9079fdd8f GIT binary patch literal 3210 zcmb7`O-L0{6vxlZdvD&nFTXXFhgcXCh_xsy0?BwmV%XP1LRz|L*`lSbbQ8E}Db&JE z7PJcjAy*3{B-)65-xqY2Od@W1yr=YE|tmppRaCscLZ{2pbf zm%6D;L-A+wUGLu1ZK9q`ZesG)wdt?#_ixnJFRi|rt5Y?X!g*eO>JCxXC-N#Tx#YFE zL^Rrw34-7{k>3z4|AxCwq^iE(ploMLJ_wp+;hjIii>l9R@mVYWTcT1OJhWT|KOd?3 zrFuGF*b=_DEWE1vg{^d+=7o1gg;!M{V~Wo|F@DT%mw9`5-v_AUx`i;YHPF6+Ta?b*6|q^JsGB zaf?dPSU{BXLC{!0lIvt=_q?f@S)7g{ zQL#(UN?G1!USz85@vX&uiGfxhEPu}oiQ2$_@%kK_HLn(C^;A}^@IqEiRDD*9*ICQ= zaS@D%-y#BI_vxjo`m7dj)z^a&ydU-XfOl(?D95XBRDISoUg|6JTvwS_<@(BgT7Bhq zuA{76SD9B;zjb}ZSi3R4FQUO`oSF*e8>;%y`1;4{8*^B*X;BB8=2q|=s`}7WzVK1_ z;ydA0)rY3?rJKT+uL`fKJ~Yl-^|jH@^$%ygA1vx)W=~ap)?@gvzB0#kn3d}@^Q!7= zeLgvV>h~W={vZcTE}0xCts`A;$rvO*qPC%)s12#Y_rF+v5Dm;D883+Pd#Zi-o`$ME zG`>C-pYIZ$l{tJX|0(lfyWv|`72JAERT;$X0hvyvXGTv+U91bmbJaQ#&NX(|AZlNg zwP)?^CmO`JQkC~4Y&TJH9pSY?F88^Fx14eCY*xDiV?!4C1K#Xo{?_r$IK6l0Wz)M> zvwNe?B;P%ZVPgi~fH!IP#hNGk`d75?!?15S=Uj9@!oF^!*Rm+?(KLwrrK 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 0000000000000000000000000000000000000000..30da12151fd98949e6ef97547ff4d0d1929e56d2 GIT binary patch literal 3710 zcmbW4L1-LR7{|Yv-OSGJPTC|*G>Ji?w4xBI2d#=wj7_9zWmB7_l+a@j9z;BN@StAW zgF+8INzop9u?pST+(-kbNn z_q`WMB#zI^TogC%OHHQakc>;>|1D{pytVR%$YgEg_~K_TpZ@vl2d^&Odj7_j>s^@} z8Sb@`q)o%SH2}1@lS9F{YurBfpOC zyQ{vvqjbu6|8C%W*?~6ii!_fKv+s}@SMmJ?8E+n~^5GsgKl;ghBjd`zPyOuHwR3(3 zSADFZk@5ZfV*K#kxZ#s-{D=>XKMMO7X{6`_?GF5E{gaG0(qQ}z-~S)^v&UziFmFi4 z2l8i+&oyV>kc6WV|^Qe1A}Xc7B9^8$Q_j9JTd}l8iTRSNrW#=4a2CPcq&<75Ksacc;q* zjC%v)_y9Eor;8D5;^NBT{?3QiUYWgXX9KD4vFCvw?EmeFK`GG4X+)zf{h)P1i$$@uX7kFg%Y_?Je)f|2B;%m(2m8M_ZTqx04fj0m1ouhC-JaO% z7yEzvQ>!0Jtsm->T0iW0&_8b{?sl19!h9$AxrF!NmL?yQj4RFGzkYOAv5mDtpa!^F z;%Y$q7Oq9F+dBrIJvmuEOYQdL?Jwlk1orc@Mtu)zxsZ&5{`>^~%oX#MWABK};(R03 zFP{6px4&?i#|IsJ&p7cH+GC1H#=%Oz{)zc%Y(B|2==|gRBCBsCRnlex1Q^orE5hiv$eupJr)Z_{CFg(ReYUYRDpCRIk%?& z)99H|-Fo~5*LrCwY>hk}eD{3nMv%-Ti(y-=`C!}hMYesrXbV~YyBaC)A6Sn~v_ftu zaZi=mt0fr+{rSaP+<#~O?Y?F6`vi8dW{7h9pWxeJVTAZy&XjAtDbq8_+UlF#`4g+1 W&hm-w%4%o+-YGcX+h literal 0 HcmV?d00001 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 + + From c1602abce12ed2d45ea58d14ab58a364964bb7aa Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 21 Mar 2024 22:48:31 +0100 Subject: [PATCH 36/42] Instance path format changed - LVS test data update needed --- testdata/lvs/double_height2.lvsdb | 2 +- testdata/lvs/double_height2_texts.lvsdb | 2 +- testdata/lvs/must_connect2.lvsdb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 From f4df6ec4ffd9cdbe473ab43bcc40bec1d9f8715c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 22 Mar 2024 19:04:13 +0100 Subject: [PATCH 37/42] Doc updates --- src/doc/doc/about/drc_ref.xml | 2 +- src/doc/doc/about/drc_ref_drc.xml | 2 +- src/doc/doc/about/drc_ref_global.xml | 37 ++++- src/doc/doc/about/drc_ref_layer.xml | 5 +- src/doc/doc/about/drc_ref_netter.xml | 45 +++++- src/doc/doc/about/drc_ref_source.xml | 2 +- src/doc/doc/about/lvs_ref.xml | 2 +- src/doc/doc/about/lvs_ref_global.xml | 2 +- src/doc/doc/about/lvs_ref_netter.xml | 2 +- src/doc/doc/manual/lvs_connect.xml | 201 +++++++++++++++++++++++++++ src/doc/docResources.qrc | 2 + 11 files changed, 285 insertions(+), 17 deletions(-) diff --git a/src/doc/doc/about/drc_ref.xml b/src/doc/doc/about/drc_ref.xml index 889c3fedc..203941a1d 100644 --- a/src/doc/doc/about/drc_ref.xml +++ b/src/doc/doc/about/drc_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_drc.xml b/src/doc/doc/about/drc_ref_drc.xml index 7ee678eca..3fab7774b 100644 --- a/src/doc/doc/about/drc_ref_drc.xml +++ b/src/doc/doc/about/drc_ref_drc.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_global.xml b/src/doc/doc/about/drc_ref_global.xml index 7e8b6ba82..45b725686 100644 --- a/src/doc/doc/about/drc_ref_global.xml +++ b/src/doc/doc/about/drc_ref_global.xml @@ -1,17 +1,12 @@ - + DRC Reference: Global Functions -

-Some functions are available on global level and can be used without any object. -Most of them are convenience functions that basically act on some default object -or provide function-like alternatives for the methods. -

"angle" - In universal DRC context: selects edges based on their orientation

@@ -1843,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_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index ce8c3aa4a..c8f124c5c 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -1,15 +1,12 @@ - + DRC Reference: Layer Object -

-The layer object represents a collection of polygons, edges or edge pairs. -

"&" - Boolean AND operation

diff --git a/src/doc/doc/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml index f03149944..bd82b4fbd 100644 --- a/src/doc/doc/about/drc_ref_netter.xml +++ b/src/doc/doc/about/drc_ref_netter.xml @@ -1,7 +1,7 @@ - + @@ -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/about/drc_ref_source.xml b/src/doc/doc/about/drc_ref_source.xml index a9ab3eaab..74d119bcb 100644 --- a/src/doc/doc/about/drc_ref_source.xml +++ b/src/doc/doc/about/drc_ref_source.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref.xml b/src/doc/doc/about/lvs_ref.xml index 4a38dd6db..58168f012 100644 --- a/src/doc/doc/about/lvs_ref.xml +++ b/src/doc/doc/about/lvs_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref_global.xml b/src/doc/doc/about/lvs_ref_global.xml index c262d6815..c1d2ad914 100644 --- a/src/doc/doc/about/lvs_ref_global.xml +++ b/src/doc/doc/about/lvs_ref_global.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref_netter.xml b/src/doc/doc/about/lvs_ref_netter.xml index f5f851203..9feef786f 100644 --- a/src/doc/doc/about/lvs_ref_netter.xml +++ b/src/doc/doc/about/lvs_ref_netter.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/manual/lvs_connect.xml b/src/doc/doc/manual/lvs_connect.xml index 501695d35..6e99e3080 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,201 @@ 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 disconnected subnets, formed by the soft connection. +

+ +

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

+
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 From 91dae8b4716c8f3bb134d50e08166283ad5a9055 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 22 Mar 2024 22:36:59 +0100 Subject: [PATCH 38/42] [consider merging] stray bug: fixed a small issue with error messages on missing resources (redundant 'ERROR') --- src/lay/lay/layHelpSource.cc | 4 ++-- src/lay/lay/layResourceHelpProvider.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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; From 554fff1d233be9f40820a0c50cd93e2df045067a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 22 Mar 2024 22:44:58 +0100 Subject: [PATCH 39/42] Added a large test case for soft connections --- src/lvs/unit_tests/lvsTests.cc | 6 ++ testdata/lvs/vexriscv_soft.lvs | 111 +++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 testdata/lvs/vexriscv_soft.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/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 + From 1874e95d67ac557ad13b7823a5b92f188a52db44 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 22 Mar 2024 23:26:52 +0100 Subject: [PATCH 40/42] Doc update --- src/doc/doc/manual/lvs_connect.xml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/doc/doc/manual/lvs_connect.xml b/src/doc/doc/manual/lvs_connect.xml index 6e99e3080..73e0cb454 100644 --- a/src/doc/doc/manual/lvs_connect.xml +++ b/src/doc/doc/manual/lvs_connect.xml @@ -407,7 +407,7 @@ connect(metal2, via1) 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 disconnected subnets, formed by the soft connection. + details about the subnets, separated by the soft connections.

@@ -481,4 +481,22 @@ soft_connect(b, a) 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. +

+
From a79d286f615fcbc25164bad5ee8755fbac39cea2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 22 Mar 2024 23:27:08 +0100 Subject: [PATCH 41/42] Cleanup --- src/db/db/dbHierNetworkProcessor.cc | 5 ++--- src/db/db/dbLayoutToNetlist.cc | 16 +++++++++------- src/db/unit_tests/dbNetlistExtractorTests.cc | 2 -- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index d05560fab..0f54e4990 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1848,7 +1848,6 @@ public: typename std::set::const_iterator cc = c; for (++cc; cc != sc->end (); ++cc) { mp_cell_clusters->join_cluster_with (*c, *cc); - // @@@ m_soft_connections.erase (std::make_pair (*c < *cc ? *c : *cc, *c < *cc ? *cc : *c)); } } @@ -2959,7 +2958,7 @@ 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 ()); // @@@ actually required? + local.remove_cluster (i->id ()); // TODO: actually required? Should work without too ... } else { @@ -2970,7 +2969,7 @@ 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); // @@@ actually required? + local.remove_cluster (other_id); // TODO: actually required? Should work without too ... } else { local.add_connection (gcid, *i); } diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 9a8fc5bbc..bc865bdc5 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -397,7 +397,7 @@ void LayoutToNetlist::join_nets (const tl::GlobPattern &cell, const std::set= 41) { MemStatisticsCollector m (false); diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 494e8dc32..0e2bf1c57 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -3356,7 +3356,6 @@ TEST(15_SoftConnections) conn.connect (rvia1, rmetal2); // extract the nets -#if 0 // @@@ std::list > jn; jn.push_back (std::set ()); @@ -3373,7 +3372,6 @@ TEST(15_SoftConnections) gp.push_back (tl::GlobPattern ("NEXT")); gp.push_back (tl::GlobPattern ("FB")); net_ex.set_joined_net_names (gp); -#endif net_ex.extract_nets (dss, 0, conn, nl, cl); From fe61627b965802456cea5f34234795f50071a823 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 08:45:38 +0100 Subject: [PATCH 42/42] Some cleanup --- src/db/db/dbHierNetworkProcessor.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 0f54e4990..bbb463faa 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -2958,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 ()); // TODO: actually required? Should work without too ... } else { @@ -2969,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); // TODO: actually required? Should work without too ... } else { local.add_connection (gcid, *i); }