diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 62d2dc2875..44b817a18a 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -473,69 +473,114 @@ void LayoutToNetlist::check_must_connect (const db::Circuit &c, const db::Net &a return; } - if (c.begin_refs () != c.end_refs ()) { + std::vector path; + 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) +{ + if (path.empty ()) { + return std::string (); + } + + 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 () + "]"; + } + + msg += "/"; + msg += c_org.name (); + + return msg; +} + +void LayoutToNetlist::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) +{ + if (c.begin_refs () != c.end_refs () && path.empty ()) { + if (a.begin_pins () == a.end_pins ()) { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s is not connected to outside")), a.expanded_name ())); + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s is not connected to outside")), a_org.expanded_name ())); error.set_cell_name (c.name ()); error.set_category_name ("must-connect"); log_entry (error); } if (b.begin_pins () == b.end_pins ()) { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s is not connected to outside")), a.expanded_name ())); + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s is not connected to outside")), a_org.expanded_name ())); error.set_cell_name (c.name ()); error.set_category_name ("must-connect"); log_entry (error); } - } else { - if (a.expanded_name () == b.expanded_name ()) { - 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.expanded_name ())); - warn.set_cell_name (c.name ()); - warn.set_category_name ("must-connect"); - log_entry (warn); + + } else if (c.begin_refs () == c.end_refs () || a.begin_pins () == a.end_pins () || b.begin_pins () == b.end_pins ()) { + + 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)); + 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)); + warn.set_cell_name (c.name ()); + warn.set_geometry (subcircuit_geometry (*path.back (), internal_layout ())); + 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 must be connected further up in the hierarchy - this is an error at chip top level")), a.expanded_name (), b.expanded_name ())); - warn.set_cell_name (c.name ()); - warn.set_category_name ("must-connect"); - log_entry (warn); + 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)); + 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)); + warn.set_cell_name (c.name ()); + warn.set_geometry (subcircuit_geometry (*path.back (), internal_layout ())); + warn.set_category_name ("must-connect"); + log_entry (warn); + } } + } if (a.begin_pins () != a.end_pins () && b.begin_pins () != b.end_pins ()) { + for (auto ref = c.begin_refs (); ref != c.end_refs (); ++ref) { + const db::SubCircuit &sc = *ref; + // TODO: consider the case of multiple pins on a net (rare) const db::Net *net_a = sc.net_for_pin (a.begin_pins ()->pin_id ()); 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.expanded_name (), c.name (), subcircuit_to_string (sc))); + 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)); error.set_cell_name (sc.circuit ()->name ()); error.set_geometry (subcircuit_geometry (sc, internal_layout ())); error.set_category_name ("must-connect"); log_entry (error); } + 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.expanded_name (), c.name (), subcircuit_to_string (sc))); + 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)); error.set_cell_name (sc.circuit ()->name ()); error.set_geometry (subcircuit_geometry (sc, internal_layout ())); error.set_category_name ("must-connect"); log_entry (error); } + if (net_a && net_b && net_a != net_b) { - if (a.expanded_name () == b.expanded_name ()) { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect nets %s of circuit %s are not connected%s")), a.expanded_name (), c.name (), subcircuit_to_string (sc))); - error.set_cell_name (sc.circuit ()->name ()); - error.set_geometry (subcircuit_geometry (sc, internal_layout ())); - error.set_category_name ("must-connect"); - log_entry (error); - } else { - db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s of circuit %s are not connected%s")), a.expanded_name (), b.expanded_name (), c.name (), subcircuit_to_string (sc))); - error.set_cell_name (sc.circuit ()->name ()); - error.set_geometry (subcircuit_geometry (sc, internal_layout ())); - error.set_category_name ("must-connect"); - log_entry (error); - } + path.push_back (&sc); + check_must_connect_impl (*sc.circuit (), *net_a, *net_b, c_org, a_org, b_org, path); + path.pop_back (); } + } + } } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 4934501940..2856de3e97 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -1041,6 +1041,7 @@ class DB_PUBLIC LayoutToNetlist void join_nets_from_pattern (db::Circuit &c, const tl::GlobPattern &p); void join_nets_from_pattern (db::Circuit &c, const std::set &p); void check_must_connect (const db::Circuit &c, const db::Net &a, const db::Net &b); + void check_must_connect_impl (const db::Circuit &c, const db::Net &a, const db::Net &b, const db::Circuit &c_org, const db::Net &a_org, const db::Net &b_org, std::vector &path); // implementation of NetlistManipulationCallbacks virtual size_t link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &trans); diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 9c7c724baa..f0f8d177ce 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -126,20 +126,6 @@ void Netlist::set_case_sensitive (bool f) m_case_sensitive = f; } -int Netlist::name_compare (bool case_sensitive, const std::string &n1, const std::string &n2) -{ - // TODO: unicode support? - if (case_sensitive) { - return strcmp (n1.c_str (), n2.c_str ()); - } else { -#if defined(_WIN32) - return _stricmp (n1.c_str (), n2.c_str ()); -#else - return strcasecmp (n1.c_str (), n2.c_str ()); -#endif - } -} - std::string Netlist::normalize_name (bool case_sensitive, const std::string &n) { if (case_sensitive) { diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 397417d041..9e52217968 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -515,11 +515,6 @@ class DB_PUBLIC Netlist */ void combine_devices (); - /** - * @brief Compares two names with the given case sensitivity - */ - static int name_compare (bool case_sensitive, const std::string &n1, const std::string &n2); - /** * @brief Normalizes a name with the given case sensitivity */ diff --git a/src/db/db/dbNetlistCompareCore.cc b/src/db/db/dbNetlistCompareCore.cc index 9e8abaef5a..49558ad4d1 100644 --- a/src/db/db/dbNetlistCompareCore.cc +++ b/src/db/db/dbNetlistCompareCore.cc @@ -1355,7 +1355,7 @@ static size_t distance3 (const NetGraphNode &a, const NetGraphNode &b1, const Ne static void analyze_nodes_for_close_matches (const std::multimap &nodes_by_edges1, const std::multimap &nodes_by_edges2, bool layout2ref, db::NetlistCompareLogger *logger, const db::NetGraph &g2) { - size_t max_search = 100; + size_t max_search = 100000; double max_fuzz_factor = 0.25; size_t max_fuzz_count = 3; size_t max_edges_split = 3; // by how many edges joining will reduce the edge count at max @@ -1368,7 +1368,9 @@ analyze_nodes_for_close_matches (const std::multimap 0; ++i) { if (i->first < min_edges) { continue; @@ -1376,7 +1378,7 @@ analyze_nodes_for_close_matches (const std::multimap seen; - for (auto j = nodes_by_edges2.begin (); j != nodes_by_edges2.end (); ++j) { + for (auto j = nodes_by_edges2.begin (); j != nodes_by_edges2.end () && tries > 0; ++j) { seen.insert (j->second); @@ -1407,7 +1409,6 @@ analyze_nodes_for_close_matches (const std::multimapfirst + k->first < i->first + max_fuzz_count + max_edges_split && tries > 0; ++k) { if (seen.find (k->second) != seen.end ()) { @@ -1436,6 +1437,8 @@ analyze_nodes_for_close_matches (const std::multimap= 21, "Analyzing failed matches"); + // Determine the range of nodes with same identity std::vector no_edges; diff --git a/src/db/db/dbNetlistCompareUtils.cc b/src/db/db/dbNetlistCompareUtils.cc index b4b50b9ca7..97951ca25b 100644 --- a/src/db/db/dbNetlistCompareUtils.cc +++ b/src/db/db/dbNetlistCompareUtils.cc @@ -125,9 +125,40 @@ const std::string &extended_net_name (const db::Net *n) } } +static int net_name_compare (bool case_sensitive, const std::string &n1, const std::string &n2) +{ + const char *n1p = n1.c_str (); + const char *n2p = n2.c_str (); + + while (*n1p && *n2p) { + + uint32_t c1 = tl::utf32_from_utf8 (n1p); + uint32_t c2 = tl::utf32_from_utf8 (n2p); + + if (! case_sensitive) { + c1 = tl::utf32_downcase (c1); + c2 = tl::utf32_downcase (c2); + } + + if (c1 != c2) { + return c1 < c2 ? -1 : 1; + } + + } + + // colon terminates net name, such that NET:I is identical to NET. + if (*n2p && *n2p != ':') { + return -1; + } else if (*n1p && *n1p != ':') { + return 1; + } else { + return 0; + } +} + int name_compare (const db::Net *a, const db::Net *b) { - return db::Netlist::name_compare (combined_case_sensitive (a->netlist (), b->netlist ()), extended_net_name (a), extended_net_name (b)); + return net_name_compare (combined_case_sensitive (a->netlist (), b->netlist ()), extended_net_name (a), extended_net_name (b)); } bool net_names_are_different (const db::Net *a, const db::Net *b) diff --git a/src/db/db/dbNetlistCompareUtils.h b/src/db/db/dbNetlistCompareUtils.h index bb4bd2948c..55614577a0 100644 --- a/src/db/db/dbNetlistCompareUtils.h +++ b/src/db/db/dbNetlistCompareUtils.h @@ -92,30 +92,30 @@ std::string nets2string (const std::pair &np); /** * @brief Derives the common case sensitivity for two netlists */ -bool combined_case_sensitive (const db::Netlist *a, const db::Netlist *b); +DB_PUBLIC bool combined_case_sensitive (const db::Netlist *a, const db::Netlist *b); /** * @brief Gets the extended net name * This name is used for comparing the net names and also employs the pin name if one is given */ -const std::string &extended_net_name (const db::Net *n); +DB_PUBLIC const std::string &extended_net_name (const db::Net *n); /** * @brief Compare two nets by name */ -int name_compare (const db::Net *a, const db::Net *b); +DB_PUBLIC int name_compare (const db::Net *a, const db::Net *b); /** * @brief Returns a value indicating whether two nets are different by name * Two unnamed nets are never different. */ -bool net_names_are_different (const db::Net *a, const db::Net *b); +DB_PUBLIC bool net_names_are_different (const db::Net *a, const db::Net *b); /** * @brief Returns a value indicating whether two nets are equal by name * Two unnamed nets are never equal. */ -bool net_names_are_equal (const db::Net *a, const db::Net *b); +DB_PUBLIC bool net_names_are_equal (const db::Net *a, const db::Net *b); // -------------------------------------------------------------------------------------------------------------------- // DeviceCompare definition and implementation diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 6d75f7be3b..edf6727dd5 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -25,6 +25,7 @@ #include "dbNetlistCompare.h" #include "dbNetlistCrossReference.h" #include "dbNetlistSpiceReader.h" +#include "dbNetlistCompareUtils.h" class NetlistCompareTestLogger : public db::NetlistCompareLogger @@ -437,6 +438,101 @@ TEST(0_EqualDeviceParameters) EXPECT_EQ (dc.less (d2, d1), false); } +TEST(0_NetNameEquivalence) +{ + db::Netlist a, b; + a.set_case_sensitive (true); + b.set_case_sensitive (false); + + EXPECT_EQ (db::combined_case_sensitive (&a, &b), false); + + b.set_case_sensitive (true); + EXPECT_EQ (db::combined_case_sensitive (&a, &b), true); + + a.set_case_sensitive (false); + EXPECT_EQ (db::combined_case_sensitive (&a, &b), false); + + db::Circuit *ca = new db::Circuit (); + ca->set_name ("C"); + a.add_circuit (ca); + + db::Circuit *cb = new db::Circuit (); + cb->set_name ("C"); + b.add_circuit (cb); + + db::Net *na = new db::Net ("net1"); + ca->add_net (na); + + db::Net *nb = new db::Net ("net1"); + cb->add_net (nb); + + EXPECT_EQ (db::name_compare (na, nb), 0); + + nb->set_name ("NET1"); + EXPECT_EQ (db::name_compare (na, nb), 0); + + nb->set_name ("NET2"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + nb->set_name ("NET11"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + nb->set_name ("net11"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + nb->set_name ("net0abc"); + EXPECT_EQ (db::name_compare (na, nb), 1); + + nb->set_name ("NET0"); + EXPECT_EQ (db::name_compare (na, nb), 1); + + a.set_case_sensitive (true); + b.set_case_sensitive (true); + + nb->set_name ("net1"); + EXPECT_EQ (db::name_compare (na, nb), 0); + + nb->set_name ("net2"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + nb->set_name ("net11"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + nb->set_name ("net0"); + EXPECT_EQ (db::name_compare (na, nb), 1); + + nb->set_name ("NET1"); + EXPECT_EQ (db::name_compare (na, nb), 1); + + na->set_name ("NET1"); + nb->set_name ("net1"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + b.set_case_sensitive (false); + + // colon terminates the net name, so that NET:I and NET and identical + + na->set_name ("NET1:I"); + nb->set_name ("net1"); + EXPECT_EQ (db::name_compare (na, nb), 0); + + na->set_name ("NET1:I"); + nb->set_name ("net1:O"); + EXPECT_EQ (db::name_compare (na, nb), -1); + + na->set_name ("NET1"); + nb->set_name ("net1:O"); + EXPECT_EQ (db::name_compare (na, nb), 0); + + na->set_name ("NET2"); + nb->set_name ("net1:O"); + EXPECT_EQ (db::name_compare (na, nb), 1); + + na->set_name ("NET1"); + nb->set_name ("net1abc:O"); + EXPECT_EQ (db::name_compare (na, nb), -1); +} + TEST(1_SimpleInverter) { const char *nls1 = diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 1985cc67cc..8c970483cc 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 @@ def connect_explicit(arg1, arg2 = nil) 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 diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index d82d5d56d4..a5603bd3f3 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -289,6 +289,12 @@ TEST(31_MustConnect2) run_test (_this, "must_connect2", "must_connect2.gds"); } +// Intermediate cell propagates must-connect pins +TEST(32_MustConnect3) +{ + run_test (_this, "must_connect3", "must_connect3.gds"); +} + // issue 1609 TEST(40_DeviceExtractorErrors) { diff --git a/testdata/lvs/double_height2.lvsdb b/testdata/lvs/double_height2.lvsdb index cb79afe2b7..a1ff6a9d4c 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(E B('Must-connect nets R of circuit INV2 are not connected') 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:$1[r0 *1 0,0]/INV2') 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 509b5b8152..049cec0979 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(E B('Must-connect nets R of circuit INV2 are not connected') 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:$1[r0 *1 0,0]/INV2') 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 b3fd8d3a3a..02ee5a88c1 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(E B('Must-connect nets VSS of circuit INV2 are not connected') 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:$2[r0 *1 0,0]/INV2') 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_connect3.cir b/testdata/lvs/must_connect3.cir new file mode 100644 index 0000000000..f9a6367013 --- /dev/null +++ b/testdata/lvs/must_connect3.cir @@ -0,0 +1,23 @@ +* Extracted by KLayout + +.SUBCKT TOP +X$2 \$1 \$I2 \$I1 \$I1 \$1.Q \$2 INV2 +X$3 \$1.A \$I3 \$2 \$I3 \$I2 \$1 INVCHAIN +.ENDS TOP + +.SUBCKT INVCHAIN IN IN2 VSS|VSS2|VSS2B OUT OUT2 VDD +X$1 VDD IN2 \$1 \$1 OUT2 VSS|VSS2|VSS2B INV2 +X$2 VDD IN \$2 \$2 OUT VSS|VSS2|VSS2B INV2 +.ENDS INVCHAIN + +.SUBCKT INV2 VDD A1 A2 Q1 Q2 VSS +X$1 VSS VDD A2 Q2 INV +X$2 VSS VDD A1 Q1 INV +.ENDS INV2 + +.SUBCKT INV \$1 \$2 \$3 \$4 +M$1 \$2 \$3 \$4 \$4 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +M$2 \$1 \$3 \$4 \$4 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/must_connect3.gds b/testdata/lvs/must_connect3.gds new file mode 100644 index 0000000000..e0115df231 Binary files /dev/null and b/testdata/lvs/must_connect3.gds differ diff --git a/testdata/lvs/must_connect3.lvs b/testdata/lvs/must_connect3.lvs new file mode 100644 index 0000000000..ac34742e72 --- /dev/null +++ b/testdata/lvs/must_connect3.lvs @@ -0,0 +1,142 @@ + +$lvs_test_source && source($lvs_test_source) + +if $lvs_test_target_lvsdb + report_lvs($lvs_test_target_lvsdb) +else + report_lvs +end + +# Implicit connection of the INV2 +# VSS nets +connect_implicit("INV2", "VSS") +connect_implicit("TOP", "VSS*") +# Fix 1 +connect_explicit("INVCHAIN", ["VSS2", "VSS2B", "VSS"]) +connect_implicit("INVCHAIN", "VDD") +connect_explicit("TOP", ["VDD"]) + +ignore_extraction_errors(true) + +writer = write_spice(true, false) +$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout") + +# needs this delegate because we use MOS3 which is not available in Spice +class SpiceReaderDelegate < RBA::NetlistSpiceReaderDelegate + + # says we want to catch these subcircuits as devices + def wants_subcircuit(name) + name == "HVNMOS" || name == "HVPMOS" + end + + # translate the element + def element(circuit, el, name, model, value, nets, params) + + if el != "M" + # all other elements are left to the standard implementation + return super + end + + if nets.size != 4 + error("Device #{model} needs four nodes") + end + + # provide a device class + cls = circuit.netlist.device_class_by_name(model) + if ! cls + cls = RBA::DeviceClassMOS3Transistor::new + cls.name = model + circuit.netlist.add(cls) + end + + # create a device + device = circuit.create_device(cls, name) + + # and configure the device + [ "S", "G", "D" ].each_with_index do |t,index| + device.connect_terminal(t, nets[index]) + end + device.set_parameter("W", params["W"] * 1e6) + device.set_parameter("L", params["L"] * 1e6) + + device + + end + +end + +reader = RBA::NetlistSpiceReader::new(SpiceReaderDelegate::new) +schematic(File.basename(source.path, ".*") + ".sch", reader) + +deep + +# Drawing layers + +nwell = input(1, 0) +active = input(2, 0) +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 +pgate = pactive & poly +psd = pactive - pgate + +active_outside_nwell = active - nwell +nactive = active_outside_nwell +ngate = nactive & poly +nsd = nactive - ngate + +# Device extraction + +# PMOS transistor device extraction +extract_devices(mos3("PMOS"), { "SD" => psd, "G" => pgate, + "tS" => psd, "tD" => psd, "tG" => poly }) + +# NMOS transistor device extraction +extract_devices(mos3("NMOS"), { "SD" => nsd, "G" => ngate, + "tS" => nsd, "tD" => nsd, "tG" => poly }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(psd, diff_cont) +connect(nsd, diff_cont) +connect(poly, poly_cont) +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") + +# Compare section + +netlist.simplify +align + +# Skip as we have errors .. +compare + diff --git a/testdata/lvs/must_connect3.lvsdb b/testdata/lvs/must_connect3.lvsdb new file mode 100644 index 0000000000..83f89832db --- /dev/null +++ b/testdata/lvs/must_connect3.lvsdb @@ -0,0 +1,482 @@ +#%lvsdb-klayout +J( + W(TOP) + U(0.001) + L(l3 '3/0') + L(l11 '3/1') + L(l6 '4/0') + L(l7 '5/0') + L(l8 '6/0') + L(l12 '6/1') + L(l9 '7/0') + L(l10 '8/0') + L(l13 '8/1') + L(l14) + L(l2) + L(l5) + C(l3 l3 l11 l7) + C(l11 l3 l11) + C(l6 l6 l8 l2 l5) + C(l7 l3 l7 l8) + C(l8 l6 l7 l8 l12 l9) + C(l12 l8 l12) + C(l9 l8 l9 l10) + C(l10 l9 l10 l13) + C(l13 l10 l13) + C(l14 l14) + C(l2 l6 l2) + C(l5 l6 l5) + G(l14 SUBSTRATE) + K(PMOS MOS3) + K(NMOS MOS3) + D(D$PMOS PMOS + T(S + R(l2 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l2 (125 -475) (775 950)) + ) + ) + D(D$NMOS NMOS + T(S + R(l5 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l5 (125 -475) (775 950)) + ) + ) + X(INV + R((-1500 -800) (3000 4600)) + N(1 + R(l6 (290 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l5 (-1375 -925) (775 950)) + ) + N(2 + R(l6 (290 2490) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l2 (-1375 -925) (775 950)) + ) + N(3 + R(l3 (-125 -250) (250 2500)) + R(l3 (-250 -3050) (250 1600)) + R(l3 (-250 1200) (250 1600)) + ) + N(4 + R(l6 (-510 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l6 (-220 2180) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -3530) (360 2840)) + R(l8 (-360 -2800) (360 760)) + R(l8 (-360 2040) (360 760)) + R(l2 (-680 -855) (775 950)) + R(l5 (-775 -3750) (775 950)) + ) + P(1) + P(2) + P(3) + P(4) + D(1 D$PMOS + Y(0 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 2) + ) + 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) + ) + ) + X(INV2 + R((0 0) (3000 9200)) + N(1 I(VDD) + R(l10 (0 3150) (3000 2900)) + R(l13 (-1890 -1450) (0 0)) + ) + N(2 I(A1) + R(l11 (1480 7110) (0 0)) + ) + N(3 I(A2) + R(l11 (1520 1950) (0 0)) + ) + N(4 I(Q1) + R(l12 (1920 7070) (0 0)) + ) + N(5 I(Q2) + R(l12 (1940 1950) (0 0)) + ) + N(6 I(VSS) + R(l13 (2680 8390) (0 0)) + R(l13 (-30 -7640) (0 0)) + ) + P(1 I(VDD)) + P(2 I(A1)) + P(3 I(A2)) + P(4 I(Q1)) + P(5 I(Q2)) + P(6 I(VSS)) + X(1 INV M O(180) Y(1500 800) + P(0 6) + P(1 1) + P(2 3) + P(3 5) + ) + X(2 INV O(180) Y(1500 8400) + P(0 6) + P(1 1) + P(2 2) + P(3 4) + ) + ) + X(INVCHAIN + R((-915 -15) (10415 9215)) + N(1 + R(l3 (7340 1650) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(2 + R(l3 (1625 1835) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(3 I(IN) + R(l3 (-90 6850) (1590 650)) + R(l11 (-700 -350) (0 0)) + ) + N(4 I(IN2) + R(l3 (5665 6790) (1590 650)) + R(l11 (-700 -350) (0 0)) + ) + N(5 I('VSS,VSS2,VSS2B') + R(l10 (-915 5290) (250 2960)) + R(l10 (-250 0) (915 250)) + R(l10 (-915 -7825) (915 250)) + R(l10 (-915 0) (250 3145)) + R(l13 (155 4305) (0 0)) + R(l13 (8990 -255) (0 0)) + R(l13 (25 -7115) (0 0)) + ) + N(6 I(OUT) + R(l12 (1890 2105) (0 0)) + ) + N(7 I(OUT2) + R(l12 (7730 2155) (0 0)) + ) + N(8 I(VDD) + R(l13 (8035 4540) (0 0)) + R(l13 (-5735 60) (0 0)) + ) + P(3 I(IN)) + P(4 I(IN2)) + P(5 I('VSS,VSS2,VSS2B')) + P(6 I(OUT)) + P(7 I(OUT2)) + P(8 I(VDD)) + X(1 INV2 Y(5780 -15) + P(0 8) + P(1 4) + P(2 1) + P(3 1) + P(4 7) + P(5 5) + ) + X(2 INV2 Y(0 0) + P(0 8) + P(1 3) + P(2 2) + P(3 2) + P(4 6) + P(5 5) + ) + ) + X(TOP + R((-305 350) (15415 9225)) + N(1 + R(l10 (3200 4800) (8800 400)) + R(l10 (-5110 -425) (0 0)) + R(l10 (-4295 30) (0 0)) + R(l10 (9270 -80) (0 0)) + ) + N(2 + R(l10 (-305 4435) (250 1220)) + R(l10 (3665 -4655) (2780 400)) + R(l10 (-2780 6900) (2815 440)) + R(l10 (-710 -250) (0 0)) + R(l10 (3675 -165) (1975 565)) + R(l10 (-1975 -8190) (1975 575)) + R(l10 (-1005 -255) (0 0)) + ) + N(3 + R(l3 (12950 2130) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(4 + R(l3 (12100 7300) (640 530)) + R(l7 (-540 -415) (270 250)) + R(l8 (-1695 -250) (1695 250)) + R(l8 (-4075 -5650) (2630 250)) + R(l8 (-250 0) (250 5150)) + ) + N(5 + R(l7 (6465 7325) (220 240)) + R(l8 (-4100 -5365) (3125 250)) + R(l8 (-250 0) (250 4860)) + R(l8 (-250 0) (1225 250)) + ) + N(6 I($1.A) + R(l11 (975 7530) (0 0)) + ) + N(7 I($1.Q) + R(l12 (13260 2010) (0 0)) + ) + X(2 INV2 Y(11365 375) + P(0 1) + P(1 4) + P(2 3) + P(3 3) + P(4 7) + P(5 2) + ) + X(3 INVCHAIN Y(610 365) + P(0 6) + P(1 5) + P(2 2) + P(3 5) + P(4 4) + P(5 1) + ) + ) +) +H( + K(PMOS MOS3) + K(NMOS MOS3) + X(INV + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I(Q)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A)) + P(4 I(Q)) + D(1 PMOS + I($1) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 1) + T(G 3) + T(D 4) + ) + D(2 NMOS + I($3) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 2) + T(G 3) + T(D 4) + ) + ) + X(INV2 + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + ) + X(2 INV I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + ) + ) + X(INVCHAIN + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + N(7 I('1')) + N(8 I('2')) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV2 I($2) + P(0 1) + P(1 2) + P(2 3) + P(3 7) + P(4 7) + P(5 4) + ) + X(2 INV2 I($3) + P(0 1) + P(1 2) + P(2 5) + P(3 8) + P(4 8) + P(5 6) + ) + ) + X(TOP + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I('1')) + N(5 I('3')) + N(6 I('2')) + N(7 I(Q)) + X(1 INVCHAIN I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + P(4 4) + P(5 5) + ) + X(2 INV2 I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + P(4 6) + P(5 7) + ) + ) +) +Z( + X(INV INV 1 + Z( + N(3 3 1) + N(4 4 1) + N(2 1 1) + N(1 2 1) + P(2 2 1) + P(3 3 1) + P(1 0 1) + P(0 1 1) + D(2 2 1) + D(1 1 1) + ) + ) + X(INV2 INV2 1 + Z( + N(2 3 1) + N(3 5 1) + N(4 4 1) + N(5 6 1) + N(1 1 1) + N(6 2 1) + P(1 2 1) + P(2 4 1) + P(3 3 1) + P(4 5 1) + P(0 0 1) + P(5 1 1) + X(2 1 1) + X(1 2 1) + ) + ) + X(INVCHAIN INVCHAIN 1 + L( + M(W B('Matching nets OUT vs. Q1 from an ambiguous group of nets')) + M(W B('Matching nets OUT2 vs. Q2 from an ambiguous group of nets')) + M(I B('Matching nets $2 vs. 1 following an ambiguous match')) + M(I B('Matching nets IN vs. A1 following an ambiguous match')) + M(I B('Matching nets $1 vs. 2 following an ambiguous match')) + M(I B('Matching nets IN2 vs. A2 following an ambiguous match')) + ) + Z( + N(2 7 1) + N(1 8 1) + N(3 3 1) + N(4 5 1) + N(6 4 W) + N(7 6 W) + N(8 1 1) + N(5 2 1) + P(0 2 1) + P(1 4 1) + P(3 3 1) + P(4 5 1) + P(5 0 1) + P(2 1 1) + X(2 1 1) + X(1 2 1) + ) + ) + X(TOP TOP 1 + Z( + N(5 4 1) + N(3 6 1) + N(4 5 1) + N(1 1 1) + N(2 2 1) + N(6 3 1) + N(7 7 1) + X(2 2 1) + X(3 1 1) + ) + ) +) diff --git a/testdata/lvs/must_connect3.sch b/testdata/lvs/must_connect3.sch new file mode 100644 index 0000000000..9114c6de9b --- /dev/null +++ b/testdata/lvs/must_connect3.sch @@ -0,0 +1,24 @@ + +.SUBCKT TOP +X$1 VDD VSS A 1 1 3 INVCHAIN +X$2 VDD VSS 3 2 2 Q INV2 +.ENDS TOP + +* cell INVCHAIN +.SUBCKT INVCHAIN VDD VSS A1 Q1 A2 Q2 +X$2 VDD VSS A1 1 1 Q1 INV2 +X$3 VDD VSS A2 2 2 Q2 INV2 +.ENDS INVCHAIN + +* cell INV2 +.SUBCKT INV2 VDD VSS A1 Q1 A2 Q2 +X$1 VDD VSS A1 Q1 INV +X$2 VDD VSS A2 Q2 INV +.ENDS INV2 + +* cell INV +.SUBCKT INV VDD VSS A Q +M$1 VDD A Q VDD PMOS L=0.25U W=0.95U +M$3 VSS A Q VSS NMOS L=0.25U W=0.95U +.ENDS INV +