diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index e3cb1553f..d0fea79ee 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -342,6 +342,30 @@ void Circuit::remove_net (Net *net) m_nets.erase (net); } +void Circuit::join_nets (Net *net, Net *with) +{ + while (with->begin_terminals () != with->end_terminals ()) { + db::Device *device = const_cast (with->begin_terminals ()->device ()); + device->connect_terminal (with->begin_terminals ()->terminal_id (), net); + } + + while (with->begin_subcircuit_pins () != with->end_subcircuit_pins ()) { + db::SubCircuit *subcircuit = const_cast (with->begin_subcircuit_pins ()->subcircuit ()); + subcircuit->connect_pin (with->begin_subcircuit_pins ()->pin_id (), net); + } + + while (with->begin_pins () != with->end_pins ()) { + connect_pin (with->begin_pins ()->pin_id (), net); + } + + // TODO: join clusters too, join net properties(?) + if (netlist ()->callbacks ()) { + netlist ()->callbacks ()->link_nets (net, with); + } + + remove_net (with); +} + void Circuit::add_device (Device *device) { device->set_circuit (this); diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index 12fa5d19b..7c1c5f727 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -416,6 +416,11 @@ public: */ void remove_net (Net *net); + /** + * @brief Joins the second net with the first one and removes the second net + */ + void join_nets (Net *net, Net *with); + /** * @brief Gets the number of nets */ diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 8ec5150ce..d362aa4c5 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -198,9 +198,23 @@ db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const return region.release (); } +void LayoutToNetlist::link_nets (const db::Net *net, const db::Net *with) +{ + if (! net->circuit () || net->circuit () != with->circuit () || ! internal_layout () + || ! internal_layout ()->is_valid_cell_index (net->circuit ()->cell_index ()) + || net->cluster_id () == 0 || with->cluster_id () == 0) { + return; + } + + connected_clusters &clusters = m_net_clusters.clusters_per_cell (net->circuit ()->cell_index ()); + clusters.join_cluster_with (net->cluster_id (), with->cluster_id ()); +} + size_t LayoutToNetlist::link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &dtrans) { - if (! subcircuit_net->circuit () || ! has_internal_layout () || ! internal_layout ()->is_valid_cell_index (parent_circuit->cell_index ())) { + if (! subcircuit_net->circuit () || ! has_internal_layout () + || ! internal_layout ()->is_valid_cell_index (parent_circuit->cell_index ()) + || subcircuit_net->cluster_id () == 0) { return 0; } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 1969e89fb..6f8771644 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -798,6 +798,7 @@ private: // 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/dbNetlist.h b/src/db/db/dbNetlist.h index 8b7bdbe4a..eb8fcaf00 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -47,6 +47,7 @@ public: virtual ~NetlistManipulationCallbacks () { } virtual size_t link_net_to_parent_circuit (const db::Net *subcircuit_net, db::Circuit *parent_circuit, const db::DCplxTrans &trans) = 0; + virtual void link_nets (const db::Net *net, const db::Net *with) = 0; }; /** diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 237fa1059..99c3cec22 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -27,20 +27,50 @@ #include "tlTimer.h" #include "tlEquivalenceClusters.h" #include "tlLog.h" +#include "tlEnv.h" +#include "tlInternational.h" #include -// verbose debug output -// TODO: make this a feature? -// #define PRINT_DEBUG_NETCOMPARE +namespace { -// verbose net graph output -// TODO: make this a feature? -// #define PRINT_DEBUG_NETGRAPH +struct GlobalCompareOptions +{ + GlobalCompareOptions () + { + m_is_initialized = false; + } -// Add this define for case insensitive compare -// (applies to circuits, device classes) -#define COMPARE_CASE_INSENSITIVE + void ensure_initialized () + { + if (! m_is_initialized) { + // $KLAYOUT_NETLIST_COMPARE_DEBUG_NETCOMPARE + debug_netcompare = tl::app_flag ("netlist-compare-debug-netcompare"); + // $KLAYOUT_NETLIST_COMPARE_DEBUG_NETGRAPH + debug_netgraph = tl::app_flag ("netlist-compare-debug-netgraph"); + // $KLAYOUT_NETLIST_COMPARE_CASE_SENSITIVE + compare_case_sensitive = tl::app_flag ("netlist-compare-case-sensitive"); + m_is_initialized = true; + } + } + + bool debug_netcompare; + bool debug_netgraph; + bool compare_case_sensitive; + +private: + bool m_is_initialized; +}; + +} + +static GlobalCompareOptions *options () +{ + // TODO: thread safe? + static GlobalCompareOptions s_options; + s_options.ensure_initialized (); + return &s_options; +} // A constant indicating a failed match const size_t failed_match = std::numeric_limits::max (); @@ -63,28 +93,25 @@ namespace db static int name_compare (const std::string &n1, const std::string &n2) { // TODO: unicode support? -#if defined(COMPARE_CASE_INSENSITIVE) + if (options ()->compare_case_sensitive) { + return strcmp (n1.c_str (), n2.c_str ()); + } else { #if defined(_WIN32) - return _stricmp (n1.c_str (), n2.c_str ()); + return _stricmp (n1.c_str (), n2.c_str ()); #else - return strcasecmp (n1.c_str (), n2.c_str ()); -#endif -#else - return strcmp (n1.c_str (), n2.c_str ()); + return strcasecmp (n1.c_str (), n2.c_str ()); #endif + } } -#if defined(COMPARE_CASE_INSENSITIVE) -static std::string normalize_name (const std::string &n) +static inline std::string normalize_name (const std::string &n) { - return tl::to_upper_case (n); + if (options ()->compare_case_sensitive) { + return n; + } else { + return tl::to_upper_case (n); + } } -#else -static inline const std::string &normalize_name (const std::string &n) -{ - return n; -} -#endif // -------------------------------------------------------------------------------------------------------------------- // DeviceCompare definition and implementation @@ -1515,11 +1542,12 @@ NetGraph::build (const db::Circuit *c, DeviceCategorizer &device_categorizer, Ci for (std::vector::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { i->apply_net_index (m_net_index); } -#if defined(PRINT_DEBUG_NETGRAPH) - for (std::vector::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { - tl::info << i->to_string () << tl::noendl; + + if (options ()->debug_netgraph) { + for (std::vector::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { + tl::info << i->to_string () << tl::noendl; + } } -#endif // create subcircuit virtual nodes @@ -1543,11 +1571,12 @@ NetGraph::build (const db::Circuit *c, DeviceCategorizer &device_categorizer, Ci for (std::map::iterator i = m_virtual_nodes.begin (); i != m_virtual_nodes.end (); ++i) { i->second.apply_net_index (m_net_index); } -#if defined(PRINT_DEBUG_NETGRAPH) - for (std::map::iterator i = m_virtual_nodes.begin (); i != m_virtual_nodes.end (); ++i) { - tl::info << i->second.to_string () << tl::noendl; + + if (options ()->debug_netgraph) { + for (std::map::iterator i = m_virtual_nodes.begin (); i != m_virtual_nodes.end (); ++i) { + tl::info << i->second.to_string () << tl::noendl; + } } -#endif } size_t @@ -1560,34 +1589,48 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr std::vector other_nodes; other_nodes.reserve (ee - e); -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent(depth) << "consider transitions:"; -#endif + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "considering transitions:"; + } + + bool first = true; for (NetGraphNode::edge_iterator i = e; i != ee; ++i) { if (i->second.first != net_index) { const NetGraphNode *nn = &node (i->second.first); -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent(depth) << "here: " << (nn->net() ? nn->net()->expanded_name().c_str() : "(null)") << " via"; - for (std::vector::const_iterator t = i->first.begin (); t != i->first.end(); ++t) { - tl::info << indent(depth) << " " << t->to_string(); + if (options ()->debug_netcompare) { + if (first) { + tl::info << indent (depth) << " here: " << (node (net_index).net () ? node (net_index).net ()->expanded_name ().c_str () : "(null)") << " ->"; + first = false; + } + tl::info << indent (depth) << " " << (nn->net () ? nn->net ()->expanded_name ().c_str() : "(null)") << " via: " << tl::noendl; + for (std::vector::const_iterator t = i->first.begin (); t != i->first.end(); ++t) { + tl::info << (t != i->first.begin () ? "; " : "") << t->to_string() << tl::noendl; + } + tl::info << ""; } -#endif nodes.push_back (nn); } } if (! nodes.empty ()) { // if non-ambiguous, non-assigned + first = true; + for (NetGraphNode::edge_iterator i = e_other; i != ee_other; ++i) { if (i->second.first != other_net_index) { const NetGraphNode *nn = &data->other->node (i->second.first); -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent(depth) << "there: " << (nn->net() ? nn->net()->expanded_name().c_str() : "(null)") << " via"; - for (std::vector::const_iterator t = i->first.begin (); t != i->first.end(); ++t) { - tl::info << indent(depth) << " " << t->to_string(); + if (options ()->debug_netcompare) { + if (first) { + tl::info << indent (depth) << " there: " << (data->other->node (other_net_index).net () ? data->other->node (other_net_index).net ()->expanded_name ().c_str () : "(null)") << " ->"; + first = false; + } + tl::info << indent(depth) << " " << (nn->net() ? nn->net()->expanded_name().c_str() : "(null)") << " via: " << tl::noendl; + for (std::vector::const_iterator t = i->first.begin (); t != i->first.end(); ++t) { + tl::info << (t != i->first.begin () ? "; " : "") << t->to_string() << tl::noendl; + } + tl::info << ""; } -#endif other_nodes.push_back (nn); } } @@ -1604,9 +1647,9 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr if (tentative) { if (nodes.size () != other_nodes.size ()) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent(depth) << "! rejected branch."; -#endif + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "=> rejected branch."; + } return failed_match; } @@ -1614,9 +1657,9 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr if (nodes.size () > 1 || other_nodes.size () > 1) { for (size_t i = 0; i < nodes.size (); ++i) { if (! (*nodes[i] == *other_nodes[i])) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent(depth) << "! rejected branch."; -#endif + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "=> rejected branch."; + } return failed_match; } } @@ -1631,9 +1674,9 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr if (bt_count == failed_match) { if (tentative) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent(depth) << "! rejected branch."; -#endif + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "=> rejected branch."; + } return bt_count; } } else { @@ -1642,11 +1685,11 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr } -#if defined(PRINT_DEBUG_NETCOMPARE) - if (! new_nodes) { - tl::info << indent(depth) << "! no updates."; + if (options ()->debug_netcompare) { + if (! new_nodes) { + tl::info << indent(depth) << "=> no updates."; + } } -#endif return new_nodes; } @@ -1671,13 +1714,13 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc size_t other_net_index = n->other_net_index (); NetGraphNode *n_other = & data->other->node (other_net_index); -#if defined(PRINT_DEBUG_NETCOMPARE) - if (! tentative) { - tl::info << indent(depth) << "deducing from pair: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name (); - } else { - tl::info << indent(depth) << "tentatively deducing from pair: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name (); + if (options ()->debug_netcompare) { + if (! tentative) { + tl::info << indent(depth) << "deducing from pair: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name (); + } else { + tl::info << indent(depth) << "tentatively deducing from pair: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name (); + } } -#endif size_t new_nodes = 0; @@ -1722,9 +1765,9 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc size_t bt_count = derive_node_identities_for_edges (e, ee, e_other, ee_other, net_index, other_net_index, depth, n_branch, tentative, with_ambiguous, data); if (bt_count == failed_match) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent(depth) << "! rejected pair."; -#endif + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "=> rejected pair."; + } return bt_count; } else { new_nodes += bt_count; @@ -1733,9 +1776,9 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc } else if (tentative) { // in tentative mode an exact match is required: no having the same edges for a node disqualifies the node // as matching. -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent(depth) << "! rejected pair for missing edge."; -#endif + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "=> rejected pair for missing edge."; + } return failed_match; } @@ -1758,9 +1801,9 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc NetGraphNode::edge_iterator e = n->find_edge (e_other->first); if (e == n->end ()) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent(depth) << "! rejected pair for missing edge."; -#endif + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "=> rejected pair for missing edge."; + } return failed_match; } @@ -1770,11 +1813,11 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc } -#if defined(PRINT_DEBUG_NETCOMPARE) - if (! tentative && new_nodes > 0) { - tl::info << indent(depth) << "finished pair deduction: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name () << " with " << new_nodes << " new pairs"; + if (options ()->debug_netcompare) { + if (! tentative && new_nodes > 0) { + tl::info << indent(depth) << "=> finished pair deduction: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name () << " with " << new_nodes << " new pairs"; + } } -#endif return new_nodes; } @@ -1855,17 +1898,18 @@ static bool net_names_are_different (const db::Net *a, const db::Net *b) size_t NetGraph::derive_node_identities_from_node_set (std::vector &nodes, std::vector &other_nodes, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, bool with_ambiguous, CompareData *data) { -#if defined(PRINT_DEBUG_NETCOMPARE) - std::string indent_s = indent (depth); - indent_s += "*" + tl::to_string (n_branch) + " "; -#endif + std::string indent_s; + if (options ()->debug_netcompare) { + indent_s = indent (depth); + indent_s += "*" + tl::to_string (n_branch) + " "; + } size_t new_nodes = 0; if (depth > data->max_depth) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent_s << "max. depth exhausted (" << depth + 1 << ">" << data->max_depth << ")"; -#endif + if (options ()->debug_netcompare) { + tl::info << indent_s << "max. depth exhausted (" << depth + 1 << ">" << data->max_depth << ")"; + } return failed_match; } @@ -1881,9 +1925,9 @@ NetGraph::derive_node_identities_from_node_set (std::vectorother, other_ni); -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent_s << "deduced match (singular): " << nodes.front ()->net ()->expanded_name () << " vs. " << other_nodes.front ()->net ()->expanded_name (); -#endif + if (options ()->debug_netcompare) { + tl::info << indent_s << "deduced match (singular): " << nodes.front ()->net ()->expanded_name () << " vs. " << other_nodes.front ()->net ()->expanded_name (); + } if (data->logger && ! tentative) { if (! (node (ni) == data->other->node (other_ni))) { // this is a mismatch but we continue, because that is the only candidate @@ -2030,9 +2074,9 @@ NetGraph::derive_node_identities_from_node_set (std::vector this favors net matching by name if (tentative && ! data->dont_consider_net_names && net_names_are_different ((*nr->n1)->net (), (*nr->n2)->net ())) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent_s << "rejecting pair as names are not identical: " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name (); -#endif + if (options ()->debug_netcompare) { + tl::info << indent_s << "rejecting pair as names are not identical: " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name (); + } return failed_match; } @@ -2044,9 +2088,9 @@ NetGraph::derive_node_identities_from_node_set (std::vectorother, other_ni); -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent_s << "deduced match (singular): " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name (); -#endif + if (options ()->debug_netcompare) { + tl::info << indent_s << "deduced match (singular): " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name (); + } if (data->logger && ! tentative) { if (! (node (ni) == data->other->node (other_ni))) { // this is a mismatch, but we continue with this @@ -2086,16 +2130,16 @@ NetGraph::derive_node_identities_from_node_set (std::vectornum * n_branch > data->max_n_branch) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent_s << "max. complexity exhausted (" << nr->num << "*" << n_branch << ">" << data->max_n_branch << ") - mismatch."; -#endif + if (options ()->debug_netcompare) { + tl::info << indent_s << "max. complexity exhausted (" << nr->num << "*" << n_branch << ">" << data->max_n_branch << ") - mismatch."; + } return failed_match; } else { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent_s << "analyzing ambiguity group with " << nr->num << " members"; -#endif + if (options ()->debug_netcompare) { + tl::info << indent_s << "analyzing ambiguity group with " << nr->num << " members"; + } // sort the ambiguity group such that net names match best @@ -2152,16 +2196,16 @@ NetGraph::derive_node_identities_from_node_set (std::vectorother, other_ni); // try this candidate in tentative mode -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent_s << "trying in tentative mode: " << (*i1)->net ()->expanded_name () << " vs. " << (*i2)->net ()->expanded_name (); -#endif + if (options ()->debug_netcompare) { + tl::info << indent_s << "trying in tentative mode: " << (*i1)->net ()->expanded_name () << " vs. " << (*i2)->net ()->expanded_name (); + } size_t bt_count = derive_node_identities (ni, depth + 1, nr->num * n_branch, &tn, with_ambiguous, data); if (bt_count != failed_match) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << indent_s << "match found"; -#endif + if (options ()->debug_netcompare) { + tl::info << indent_s << "match found"; + } // we have a match ... if (any) { @@ -2185,9 +2229,9 @@ NetGraph::derive_node_identities_from_node_set (std::vectordebug_netcompare) { + tl::info << indent_s << "mismatch."; + } // a mismatch - stop here. return failed_match; } @@ -2207,13 +2251,13 @@ NetGraph::derive_node_identities_from_node_set (std::vectorother, other_ni); -#if defined(PRINT_DEBUG_NETCOMPARE) - if (equivalent_other_nodes.has_attribute (p->second)) { - tl::info << indent_s << "deduced ambiguous match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name (); - } else { - tl::info << indent_s << "deduced match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name (); + if (options ()->debug_netcompare) { + if (equivalent_other_nodes.has_attribute (p->second)) { + tl::info << indent_s << "deduced ambiguous match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name (); + } else { + tl::info << indent_s << "deduced match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name (); + } } -#endif if (data->logger) { bool ambiguous = equivalent_other_nodes.has_attribute (p->second); if (ambiguous) { @@ -2249,9 +2293,9 @@ NetGraph::derive_node_identities_from_node_set (std::vectornum << " members"; -#endif + if (options ()->debug_netcompare) { + tl::info << indent_s << "finished analysis of ambiguity group with " << nr->num << " members"; + } } @@ -2516,9 +2560,9 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const if (all_subcircuits_verified (ca, verified_circuits_a) && all_subcircuits_verified (cb, verified_circuits_b)) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "treating circuit: " << ca->name () << " vs. " << cb->name (); -#endif + if (options ()->debug_netcompare) { + tl::info << "treating circuit: " << ca->name () << " vs. " << cb->name (); + } if (mp_logger) { mp_logger->begin_circuit (ca, cb); } @@ -2835,9 +2879,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, g2.identify (ni2, ni1); } -#if defined(PRINT_DEBUG_NETCOMPARE) int iter = 0; -#endif // two passes: one without ambiguities, the second one with @@ -2845,20 +2887,20 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (int pass = 0; pass < 2; ++pass) { -#if defined(PRINT_DEBUG_NETCOMPARE) - if (pass > 0) { - tl::info << "including ambiguous nodes now."; + if (options ()->debug_netcompare) { + if (pass > 0) { + tl::info << "including ambiguous nodes now."; + } } -#endif good = true; while (true) { -#if defined(PRINT_DEBUG_NETCOMPARE) ++iter; - tl::info << "new compare iteration #" << iter; - tl::info << "deducing from present nodes ..."; -#endif + if (options ()->debug_netcompare) { + tl::info << "new compare iteration #" << iter; + tl::info << "deducing from present nodes ..."; + } size_t new_identities = 0; @@ -2877,18 +2919,18 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, size_t ni = g1.derive_node_identities (i1 - g1.begin (), 0, 1, 0 /*not tentative*/, pass > 0 /*with ambiguities*/, &data); if (ni > 0 && ni != failed_match) { new_identities += ni; -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << ni << " new identities."; -#endif + if (options ()->debug_netcompare) { + tl::info << ni << " new identities."; + } } } } -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "checking topological identity ..."; -#endif + if (options ()->debug_netcompare) { + tl::info << "checking topological identity ..."; + } // derive new identities through topology: first collect all nets with the same topological signature @@ -2934,9 +2976,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, 0, 1, 0 /*not tentative*/, pass > 0 /*with ambiguities*/, &data); if (ni > 0 && ni != failed_match) { new_identities += ni; -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << ni << " new identities."; -#endif + if (options ()->debug_netcompare) { + tl::info << ni << " new identities."; + } } if (new_identities == 0) { @@ -3585,4 +3627,171 @@ NetlistComparer::do_subcircuit_assignment (const db::Circuit *c1, const db::NetG } } +static bool derive_symmetry_groups (const db::NetGraph &graph, const tl::equivalence_clusters &identical_nodes, std::set &considered_nodes, const std::set &symmetry_group, std::list > &symmetry_groups) +{ + std::set cids; + std::set new_symmetry_group; + NetGraphNode::edge_type::first_type edge; + + std::vector common_nodes_first; + + bool has_candidate = true; + + for (std::set::const_iterator g = symmetry_group.begin (); g != symmetry_group.end () && has_candidate; ++g) { + + std::vector common_nodes; + + const NetGraphNode &n = graph.node (*g); + for (NetGraphNode::edge_iterator e = n.begin (); e != n.end () && has_candidate; ++e) { + + if (considered_nodes.find (e->second.first) != considered_nodes.end ()) { + continue; + } + + const NetGraphNode *other = &graph.node (e->second.first); + // NOTE: nodes with pins don't go into a symmetry group as we don't know what the pins connect to + if (other->net ()->pin_count () == 0 && identical_nodes.has_attribute (other)) { + + if (cids.empty ()) { + edge = e->first; + } else if (edge != e->first) { + has_candidate = false; + } + + if (has_candidate) { + cids.insert (identical_nodes.cluster_id (other)); + if (cids.size () > 1) { + has_candidate = false; + } else { + new_symmetry_group.insert (e->second.first); + } + } + + } else { + common_nodes.push_back (*e); + } + + } + + if (has_candidate) { + + // all other edges need to have identical destinations for the symmetry group to be + // actually symmetric + std::sort (common_nodes.begin (), common_nodes.end ()); + if (g == symmetry_group.begin ()) { + common_nodes_first.swap (common_nodes); + } else if (common_nodes_first != common_nodes) { + has_candidate = false; + } + + } + + } + + if (has_candidate && ! cids.empty () && new_symmetry_group.size () > 1) { + + considered_nodes.insert (new_symmetry_group.begin (), new_symmetry_group.end ()); + + if (derive_symmetry_groups (graph, identical_nodes, considered_nodes, new_symmetry_group, symmetry_groups)) { + + symmetry_groups.push_back (new_symmetry_group); + + } else { + + std::set cn; + std::set_difference (considered_nodes.begin (), considered_nodes.end (), new_symmetry_group.begin (), new_symmetry_group.end (), std::inserter (cn, cn.begin ())); + considered_nodes.swap (cn); + + has_candidate = false; + + } + + } + + return has_candidate; +} + +void +NetlistComparer::join_symmetric_nets (db::Circuit *circuit) +{ + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Join symmetric nodes for circuit: ")) + circuit->name ()); + + db::DeviceFilter device_filter (m_cap_threshold, m_res_threshold); + db::CircuitPinMapper circuit_pin_mapper; + std::map circuit_and_pin_mapping; + + db::NetGraph graph; + graph.build (circuit, *mp_device_categorizer, *mp_circuit_categorizer, device_filter, &circuit_and_pin_mapping, &circuit_pin_mapper); + + // sort the nodes so we can easily identify the identical ones (in terms of topology) + // nodes are identical if the attached devices and circuits are of the same kind and with the same parameters + // and connect to other nodes in identical configurations. + + std::vector nodes; + + nodes.reserve (graph.end () - graph.begin ()); + for (db::NetGraph::node_iterator i = graph.begin (); i != graph.end (); ++i) { + if (! i->has_other () && i->net ()) { + nodes.push_back (i.operator-> ()); + } + } + + std::sort (nodes.begin (), nodes.end (), CompareNodePtr ()); + + // Identical nodes leading to the same nodes on the other side are candidates for symmetry. + + tl::equivalence_clusters identical_nodes; + + for (std::vector::const_iterator np = nodes.begin (); np + 1 != nodes.end (); ++np) { + if (*np[0] == *np[1]) { + identical_nodes.same (np[0], np[1]); + } + } + + std::list > symmetry_groups; + std::set visited; + + for (std::vector::const_iterator np = nodes.begin (); np != nodes.end (); ++np) { + + size_t node_id = graph.node_index_for_net (np[0]->net ()); + if (visited.find (node_id) != visited.end ()) { + continue; + } + + std::set considered_nodes; + considered_nodes.insert (node_id); + + std::set symmetry_group; + symmetry_group.insert (node_id); + + derive_symmetry_groups (graph, identical_nodes, considered_nodes, symmetry_group, symmetry_groups); + + visited.insert (considered_nodes.begin (), considered_nodes.end ()); + + } + + if (! symmetry_groups.empty () && tl::verbosity () >= 30) { + tl::info << tl::to_string (tr ("Symmetry groups:")); + int index = 0; + for (std::list >::const_iterator g = symmetry_groups.begin (); g != symmetry_groups.end (); ++g) { + tl::info << " [" << index << "] " << tl::noendl; + for (std::set::const_iterator i = g->begin (); i != g->end (); ++i) { + tl::info << (i == g->begin () ? "" : ",") << (graph.node (*i).net () ? graph.node (*i).net ()->expanded_name ().c_str () : "(null)") << tl::noendl; + } + ++index; + tl::info << ""; + } + } + + // join the nets + + for (std::list >::const_iterator g = symmetry_groups.begin (); g != symmetry_groups.end (); ++g) { + for (std::set::const_iterator i = g->begin (); i != g->end (); ++i) { + if (i != g->begin ()) { + circuit->join_nets (const_cast (graph.net_by_node_index (*g->begin ())), const_cast (graph.net_by_node_index (*i))); + } + } + } +} + } diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index 0ba957017..d639ed12f 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -302,6 +302,20 @@ public: */ bool compare (const db::Netlist *a, const db::Netlist *b, db::NetlistCompareLogger *logger) const; + /** + * @brief Joins symmetric nodes in the given circuit + * + * Nodes are symmetric if their exchanging would not modify the circuit. + * Hence they will carry the same potential and can be connected (joined). + * This will simplify the circuit and can be applied before device combination + * to render a schematic-equivalent netlist in some cases (split gate option). + * + * This algorithm will apply the comparer's settings to the symmetry + * condition (device filtering, device compare tolerances, device class + * equivalence etc.). + */ + void join_symmetric_nets (db::Circuit *circuit); + private: // No copying NetlistComparer (const NetlistComparer &); diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 56fc451ea..79a91fc2c 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1201,6 +1201,12 @@ Class decl_dbCircuit (decl_dbNetlistObject, "db", "Circuit", gsi::method ("remove_net", &db::Circuit::remove_net, gsi::arg ("net"), "@brief Removes the given net from the circuit\n" ) + + gsi::method ("join_nets", &db::Circuit::join_nets, gsi::arg ("net"), gsi::arg ("with"), + "@brief Joins (connects) two nets into one\n" + "This method will connect the 'with' net with 'net' and remove 'with'.\n" + "\n" + "This method has been introduced in version 0.26.4." + ) + gsi::iterator ("each_net", (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::begin_nets, (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::end_nets, "@brief Iterates over the nets of the circuit" ) + @@ -1412,6 +1418,22 @@ static void blank_circuit_by_name (db::Netlist *nl, const std::string &name_patt } } +static std::vector +circuits_by_name (db::Netlist *netlist, const std::string &name_pattern) +{ + std::vector res; + + tl::GlobPattern glob (name_pattern); + for (db::Netlist::circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { + db::Circuit *circuit = c.operator-> (); + if (glob.match (circuit->name ())) { + res.push_back (circuit); + } + } + + return res; +} + Class decl_dbNetlist ("db", "Netlist", gsi::method_ext ("add", &gsi::add_circuit, gsi::arg ("circuit"), "@brief Adds the circuit to the netlist\n" @@ -1469,6 +1491,12 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Gets the circuit object for a given name.\n" "If the name is not a valid circuit name, nil is returned." ) + + gsi::method_ext ("circuits_by_name", &circuits_by_name, gsi::arg ("name_pattern"), + "@brief Gets the circuit objects for a given name filter.\n" + "The name filter is a glob pattern. This method will return all \\Circuit objects matching the glob pattern.\n" + "\n" + "This method has been introduced in version 0.26.4.\n" + ) + gsi::iterator ("each_circuit_top_down", (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_top_down, (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_top_down, "@brief Iterates over the circuits top-down\n" "Iterating top-down means the parent circuits come before the child circuits. " diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index 31e4275a2..df91db260 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -557,6 +557,20 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "This method will perform the actual netlist compare using the given logger. It will return true if both netlists are identical. " "If the comparer has been configured with \\same_nets or similar methods, the objects given there must " "be located inside 'circuit_a' and 'circuit_b' respectively." + ) + + gsi::method ("join_symmetric_nets", &db::NetlistComparer::join_symmetric_nets, gsi::arg ("circuit"), + "@brief Joins symmetric nodes in the given circuit.\n" + "\n" + "Nodes are symmetrical if swapping them would not modify the circuit.\n" + "Hence they will carry the same potential and can be connected (joined).\n" + "This will simplify the circuit and can be applied before device combination\n" + "to render a schematic-equivalent netlist in some cases (split gate option).\n" + "\n" + "This algorithm will apply the comparer's settings to the symmetry\n" + "condition (device filtering, device compare tolerances, device class\n" + "equivalence etc.).\n" + "\n" + "This method has been introduced in version 0.26.4.\n" ), "@brief Compares two netlists\n" "This class performs a comparison of two netlists.\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 4e901a9ff..f352bdb0c 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -584,6 +584,75 @@ TEST(1_BasicExtraction) EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I39"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2"); + + // use this opportunity to check joining of nets with cluster joining + db::Circuit *top = l2n.netlist ()->circuit_by_name ("RINGO"); + top->join_nets (top->net_by_name ("VSS"), top->net_by_name ("VDD")); + top->join_nets (top->net_by_name ("FB"), top->net_by_name ("OSC")); + + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), + "circuit RINGO (FB=FB,OSC=FB,VSS=VSS,VDD=VDD);\n" + " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=FB,$4=VSS,$5=VSS);\n" + " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VSS);\n" + " subcircuit INV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VSS);\n" + " subcircuit INV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VSS);\n" + " subcircuit INV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VSS);\n" + " subcircuit INV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VSS);\n" + " subcircuit INV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VSS);\n" + " subcircuit INV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VSS);\n" + " subcircuit INV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VSS);\n" + " subcircuit INV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VSS);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n" + ); + + // do some probing after purging + + // top level + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS"); + + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I39"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2"); + + // compare the collected test data + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_joined_nets.gds"); + + db::compare_layouts (_this, ly2, au); + } } TEST(2_Probing) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index db3f02048..fe6749094 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -3703,3 +3703,204 @@ TEST(24_NodesRemovedButConnectedInOther) ); EXPECT_EQ (good, true); } + +TEST(25_JoinSymmetricNets) +{ + const char *nls = + "circuit NAND2 (A=A,B=B,OUT=OUT,VSS=VSS,VDD=VDD);\n" + " device PMOS $1 (S=OUT,G=A,D=VDD) (L=0.25,W=1);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=1);\n" + // NOTE: $1 and $2 are separate nets, but can be joined due to symmetry + " device NMOS $3 (S=$1,G=A,D=OUT) (L=0.25,W=1);\n" + " device NMOS $4 (S=$2,G=A,D=OUT) (L=0.25,W=1);\n" + " device NMOS $5 (S=$1,G=B,D=VSS) (L=0.25,W=1);\n" + " device NMOS $6 (S=$2,G=B,D=VSS) (L=0.25,W=1);\n" + "end;\n"; + + db::Netlist nl; + prep_nl (nl, nls); + + db::NetlistComparer comp; + comp.join_symmetric_nets (nl.circuit_by_name ("NAND2")); + + // NOTE $1 and $2 are joined because they are symmetric + EXPECT_EQ (nl.to_string (), + "circuit NAND2 (A=A,B=B,OUT=OUT,VSS=VSS,VDD=VDD);\n" + " device PMOS $1 (S=OUT,G=A,D=VDD) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $3 (S=$1,G=A,D=OUT) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $4 (S=$1,G=A,D=OUT) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $5 (S=$1,G=B,D=VSS) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $6 (S=$1,G=B,D=VSS) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ) +} + +TEST(25b_JoinSymmetricNetsMultiple) +{ + const char *nls = + "circuit NAND2 (A=A,B=B,OUT=OUT,VSS=VSS,VDD=VDD);\n" + " device PMOS $1 (S=OUT,G=A,D=VDD) (L=0.25,W=1);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=1);\n" + " device PMOS $3 (S=VDD,G=C,D=OUT) (L=0.25,W=1);\n" + // NOTE: $1 and $2 are separate nets, but can be joined due to symmetry + " device NMOS $4 (S=$1,G=A,D=OUT) (L=0.25,W=1);\n" + " device NMOS $5 (S=$2,G=A,D=OUT) (L=0.25,W=1);\n" + " device NMOS $6 (S=$3,G=A,D=OUT) (L=0.25,W=1);\n" + " device NMOS $7 (S=$1,G=B,D=$4) (L=0.25,W=1);\n" + " device NMOS $8 (S=$2,G=B,D=$5) (L=0.25,W=1);\n" + " device NMOS $9 (S=$3,G=B,D=$6) (L=0.25,W=1);\n" + " device NMOS $10 (S=$4,G=C,D=VSS) (L=0.25,W=1);\n" + " device NMOS $11 (S=$5,G=C,D=VSS) (L=0.25,W=1);\n" + " device NMOS $12 (S=$6,G=C,D=VSS) (L=0.25,W=1);\n" + "end;\n"; + + db::Netlist nl; + prep_nl (nl, nls); + + db::NetlistComparer comp; + comp.join_symmetric_nets (nl.circuit_by_name ("NAND2")); + + nl.combine_devices (); + + // NOTE $1 and $2 are joined because they are symmetric + EXPECT_EQ (nl.to_string (), + "circuit NAND2 (A=A,B=B,OUT=OUT,VSS=VSS,VDD=VDD);\n" + " device PMOS $1 (S=OUT,G=A,D=VDD) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $3 (S=VDD,G=C,D=OUT) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $4 (S=$1,G=A,D=OUT) (L=0.25,W=3,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $7 (S=$1,G=B,D=$4) (L=0.25,W=3,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $10 (S=$4,G=C,D=VSS) (L=0.25,W=3,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ) +} + +TEST(26_JoinSymmetricNets) +{ + const char *nls = + "circuit RESCUBE (A=A,B=B);\n" + " device RES $1 (A=A,B=$1) (R=1000);\n" + " device RES $2 (A=A,B=$2) (R=1000);\n" + " device RES $3 (A=A,B=$3) (R=1000);\n" + " device RES $4 (A=$1,B=$4) (R=1000);\n" + " device RES $5 (A=$2,B=$4) (R=1000);\n" + " device RES $6 (A=$2,B=$5) (R=1000);\n" + " device RES $7 (A=$3,B=$5) (R=1000);\n" + " device RES $8 (A=$3,B=$6) (R=1000);\n" + " device RES $9 (A=$1,B=$6) (R=1000);\n" + " device RES $9 (A=$4,B=B) (R=1000);\n" + " device RES $10 (A=$5,B=B) (R=1000);\n" + " device RES $11 (A=$6,B=B) (R=1000);\n" + "end;\n"; + + db::Netlist nl; + prep_nl (nl, nls); + + db::NetlistComparer comp; + comp.join_symmetric_nets (nl.circuit_by_name ("RESCUBE")); + + EXPECT_EQ (nl.to_string (), + "circuit RESCUBE (A=A,B=B);\n" + " device RES $1 (A=A,B=$1) (R=1000,L=0,W=0,A=0,P=0);\n" + " device RES $2 (A=A,B=$1) (R=1000,L=0,W=0,A=0,P=0);\n" + " device RES $3 (A=A,B=$1) (R=1000,L=0,W=0,A=0,P=0);\n" + " device RES $4 (A=$1,B=$4) (R=1000,L=0,W=0,A=0,P=0);\n" + " device RES $5 (A=$1,B=$4) (R=1000,L=0,W=0,A=0,P=0);\n" + " device RES $6 (A=$1,B=$4) (R=1000,L=0,W=0,A=0,P=0);\n" + " device RES $7 (A=$1,B=$4) (R=1000,L=0,W=0,A=0,P=0);\n" + " device RES $8 (A=$1,B=$4) (R=1000,L=0,W=0,A=0,P=0);\n" + " device RES $9 (A=$1,B=$4) (R=1000,L=0,W=0,A=0,P=0);\n" + " device RES $10 (A=$4,B=B) (R=1000,L=0,W=0,A=0,P=0);\n" + " device RES $11 (A=$4,B=B) (R=1000,L=0,W=0,A=0,P=0);\n" + " device RES $12 (A=$4,B=B) (R=1000,L=0,W=0,A=0,P=0);\n" + "end;\n" + ) + + nl.combine_devices (); + EXPECT_EQ (nl.to_string (), + "circuit RESCUBE (A=A,B=B);\n" + " device RES $10 (A=A,B=B) (R=833.333333333,L=0,W=0,A=0,P=0);\n" + "end;\n" + ) +} + +TEST(27_DontJoinSymmetricNetsWithPins) +{ + const char *nls = + "circuit NAND2 (A=A,B=B,OUT=OUT,VSS=VSS,VDD=VDD,X=$1,Y=$2);\n" + " device PMOS $1 (S=OUT,G=A,D=VDD) (L=0.25,W=1);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=1);\n" + // NOTE: $1 and $2 are separate nets, but can be joined due to symmetry + " device NMOS $3 (S=$1,G=A,D=OUT) (L=0.25,W=1);\n" + " device NMOS $4 (S=$2,G=A,D=OUT) (L=0.25,W=1);\n" + " device NMOS $5 (S=$1,G=B,D=VSS) (L=0.25,W=1);\n" + " device NMOS $6 (S=$2,G=B,D=VSS) (L=0.25,W=1);\n" + "end;\n"; + + db::Netlist nl; + prep_nl (nl, nls); + + db::NetlistComparer comp; + comp.join_symmetric_nets (nl.circuit_by_name ("NAND2")); + + // NOTE $1 and $2 are NOT joined because they have pins + EXPECT_EQ (nl.to_string (), + "circuit NAND2 (A=A,B=B,OUT=OUT,VSS=VSS,VDD=VDD,X=$1,Y=$2);\n" + " device PMOS $1 (S=OUT,G=A,D=VDD) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $3 (S=$1,G=A,D=OUT) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $4 (S=$2,G=A,D=OUT) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $5 (S=$1,G=B,D=VSS) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $6 (S=$2,G=B,D=VSS) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ) +} + +TEST(28_NoSymmetryDetectionCases) +{ + { + const char *nls = + "circuit NAND2 (A=A,B=B,OUT=OUT,VSS=VSS,VDD=VDD);\n" + " device PMOS $1 (S=OUT,G=A,D=VDD) (L=0.25,W=1);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=1);\n" + // NOTE: $1 and $2 are separate nets, but cannot be joined because of different W + " device NMOS $3 (S=$1,G=A,D=OUT) (L=0.25,W=1);\n" + " device NMOS $4 (S=$2,G=A,D=OUT) (L=0.25,W=1.1);\n" + " device NMOS $5 (S=$1,G=B,D=VSS) (L=0.25,W=1);\n" + " device NMOS $6 (S=$2,G=B,D=VSS) (L=0.25,W=1);\n" + "end;\n"; + + db::Netlist nl; + prep_nl (nl, nls); + + db::NetlistComparer comp; + std::string sref = nl.to_string (); + comp.join_symmetric_nets (nl.circuit_by_name ("NAND2")); + + EXPECT_EQ (nl.to_string (), sref); + } + + { + const char *nls = + "circuit NAND2 (A=A,B=B,OUT=OUT,VSS=VSS,VSS2=VSS2,VDD=VDD);\n" + " device PMOS $1 (S=OUT,G=A,D=VDD) (L=0.25,W=1);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=1);\n" + " device NMOS $3 (S=$1,G=A,D=OUT) (L=0.25,W=1);\n" + " device NMOS $4 (S=$2,G=A,D=OUT) (L=0.25,W=1);\n" + // NOTE: $1 and $2 are separate nets, but cannot be joined because of different D connections + " device NMOS $5 (S=$1,G=B,D=VSS) (L=0.25,W=1);\n" + " device NMOS $6 (S=$2,G=B,D=VSS2) (L=0.25,W=1);\n" + "end;\n"; + + db::Netlist nl; + prep_nl (nl, nls); + + db::NetlistComparer comp; + std::string sref = nl.to_string (); + comp.join_symmetric_nets (nl.circuit_by_name ("NAND2")); + + EXPECT_EQ (nl.to_string (), sref); + } +} + diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index e7745577f..8cff11d1e 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -371,15 +371,6 @@ TEST(1_DeviceAndNetExtraction) " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" "end;\n" ); - - // compare the collected test data - - std::string au = tl::testsrc (); - au = tl::combine_path (au, "testdata"); - au = tl::combine_path (au, "algo"); - au = tl::combine_path (au, "device_extract_au1.gds"); - - db::compare_layouts (_this, ly, au); } TEST(2_DeviceAndNetExtractionFlat) diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 146c33f5b..9f66d06f0 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -1525,3 +1525,89 @@ TEST(23_NetlistObject) pin3 = pin2; EXPECT_EQ (pin3.property (1).to_string (), "hello"); } + +TEST(24_JoinNets) +{ + db::Netlist nl; + db::Circuit *c; + + db::DeviceClass *dc; + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("NMOS"); + nl.add_device_class (dc); + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("PMOS"); + nl.add_device_class (dc); + + nl.from_string ( + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);\n" + " subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n" + " subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit PTRANS ($1=$1,$2=$2,$3=$3);\n" + " device PMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n" + "end;\n" + "circuit NTRANS ($1=$1,$2=$2,$3=$3);\n" + " device NMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n" + "end;\n" + ); + + c = nl.circuit_by_name ("INV2"); + c->join_nets (c->net_by_name ("IN"), c->net_by_name ("OUT")); + + EXPECT_EQ (nl.to_string (), + "circuit INV2 (IN=IN,$2=$2,OUT=IN,$4=$4,$5=$5);\n" + " subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);\n" + " subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n" + " subcircuit PTRANS SC3 ($1=$5,$2=IN,$3=$2);\n" + " subcircuit NTRANS SC4 ($1=$4,$2=IN,$3=$2);\n" + "end;\n" + "circuit PTRANS ($1=$1,$2=$2,$3=$3);\n" + " device PMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + "circuit NTRANS ($1=$1,$2=$2,$3=$3);\n" + " device NMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); +} + +TEST(25_JoinNets) +{ + db::Netlist nl; + db::Circuit *c; + + db::DeviceClass *dc; + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("NMOS"); + nl.add_device_class (dc); + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("PMOS"); + nl.add_device_class (dc); + + nl.from_string ( + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $3 (S=$4,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + c = nl.circuit_by_name ("INV2"); + c->join_nets (c->net_by_name ("IN"), c->net_by_name ("OUT")); + + EXPECT_EQ (nl.to_string (), + "circuit INV2 (IN=IN,$2=$2,OUT=IN,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S=$5,G=$2,D=IN) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $3 (S=$4,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $4 (S=$4,G=$2,D=IN) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); +} diff --git a/src/lay/lay/doc/about/drc_ref.xml b/src/lay/lay/doc/about/drc_ref.xml index a4a9cf55f..72a5658de 100644 --- a/src/lay/lay/doc/about/drc_ref.xml +++ b/src/lay/lay/doc/about/drc_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml index 5d22e240b..4d0e8c273 100644 --- a/src/lay/lay/doc/about/drc_ref_global.xml +++ b/src/lay/lay/doc/about/drc_ref_global.xml @@ -1,7 +1,7 @@ - + @@ -151,7 +151,7 @@ l1and2, l1minus2 = cheat("UNIT_CELL) do end

-(Technically, the check code block is a Ruby Proc and cannot create variables +(Technically, the cheat code block is a Ruby Proc and cannot create variables outside it's scope. Hence the results of this code block have to be passed through the "cheat" method).

@@ -234,7 +234,6 @@ See Netter#connect_global

Usage:

  • connect_implicit(label_pattern)
  • -
  • connect_implicit(cell_pattern, label_pattern)

See Netter#connect_implicit for a description of that function. @@ -769,7 +768,7 @@ a new target will be set up.

-Use the first version of this method to supply label strings which create implicit net connections -on the top level circuit. This feature is useful to connect identically labelled nets +Use this method to supply label strings which create implicit net connections +on the top level circuit in the first version. This feature is useful to connect identically labelled nets while a component isn't integrated yet. If the component is integrated, nets may be connected on a higher hierarchy level - e.g. by a power mesh. Inside the component this net consists of individual islands. To properly perform netlist extraction and comparison, these islands need to be connected even though there isn't a physical connection. "connect_implicit" can -achieve this if these islands are labelled with the same text on the top level of the +achive this if these islands are labelled with the same text on the top level of the component.

-In the second version, the pattern can be specified for a cell range (given by a cell glob-style -name pattern or a single cell name). These pattern are applied to non-top cells. An unspecific pattern +In the second version, the pattern can be specified for a cell range (given by a cell name pattern or a +single cell name). These pattern are applied to non-top cells. The unspecific pattern has priority over the cell-specific ones. As the cell selector is a pattern itself, a single cell may fall into more than one category. In this case, the label filters are combined. diff --git a/src/lay/lay/doc/about/drc_ref_source.xml b/src/lay/lay/doc/about/drc_ref_source.xml index 04ee3c660..3a2b42bea 100644 --- a/src/lay/lay/doc/about/drc_ref_source.xml +++ b/src/lay/lay/doc/about/drc_ref_source.xml @@ -1,7 +1,7 @@ - + diff --git a/src/lay/lay/doc/about/lvs_ref.xml b/src/lay/lay/doc/about/lvs_ref.xml index 1fcdb8ba6..9a5817a14 100644 --- a/src/lay/lay/doc/about/lvs_ref.xml +++ b/src/lay/lay/doc/about/lvs_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/lay/lay/doc/about/lvs_ref_global.xml b/src/lay/lay/doc/about/lvs_ref_global.xml index 4ea1fa4fb..970b72954 100644 --- a/src/lay/lay/doc/about/lvs_ref_global.xml +++ b/src/lay/lay/doc/about/lvs_ref_global.xml @@ -1,7 +1,7 @@ - + @@ -46,6 +46,15 @@ See Netter#compare for a descrip

See Netter#equivalent_pins for a description of that function.

+

"join_symmetric_nets" - Joins symmetric nets of selected circuits on the extracted netlist

+ +

Usage:

+
    +
  • join_symmetric_nets(circuit_filter)
  • +
+

+See Netter#join_symmetric_nets for a description of that function. +

"max_branch_complexity" - Configures the maximum branch complexity for ambiguous net matching

Usage:

diff --git a/src/lay/lay/doc/about/lvs_ref_netter.xml b/src/lay/lay/doc/about/lvs_ref_netter.xml index 604805c0c..3e22ca91d 100644 --- a/src/lay/lay/doc/about/lvs_ref_netter.xml +++ b/src/lay/lay/doc/about/lvs_ref_netter.xml @@ -1,7 +1,7 @@ - + @@ -110,6 +110,29 @@ If the netlist provides named pins, names can be used instead of numbers. Before this method can be used, a schematic netlist needs to be loaded with
schematic.

+

"join_symmetric_nets" - Joins symmetric nets of selected circuits on the extracted netlist

+ +

Usage:

+
    +
  • join_symmetric_nets(circuit_filter)
  • +
+

+Nets are symmetrical if swapping them would not modify the circuit. +Hence they will carry the same potential and can be connected (joined). +This will simplify the circuit and can be applied before device combination +(e.g. through "netlist.simplify") to render a schematic-equivalent netlist in some +cases where symmetric nodes are split (i.e. "split gate" configuration). +

+This method operates on the extracted netlist (layout). The circuit filter +specifies the circuits to which to apply this operation. The filter is a +glob-style pattern. Using "*" for all circuits is possible, but it's +discouraged currenty until the reliability of the symmetry detection +algorithm is established. Currently it is recommended to apply it only to +those circuits for which this feature is required. +

+For the symmetry detection, the specified constraints (e.g. tolerances, +device filters etc.) apply. +

"lvs_data" - Gets the internal LayoutVsSchematic object

Usage:

diff --git a/src/lay/lay/doc/images/drc_and1.png b/src/lay/lay/doc/images/drc_and1.png index 9ea749e22..5964e1939 100644 Binary files a/src/lay/lay/doc/images/drc_and1.png and b/src/lay/lay/doc/images/drc_and1.png differ diff --git a/src/lay/lay/doc/images/drc_and2.png b/src/lay/lay/doc/images/drc_and2.png index 87bd69131..f6a9cfe6b 100644 Binary files a/src/lay/lay/doc/images/drc_and2.png and b/src/lay/lay/doc/images/drc_and2.png differ diff --git a/src/lay/lay/doc/images/drc_and3.png b/src/lay/lay/doc/images/drc_and3.png index ccb06bbc0..08ba54228 100644 Binary files a/src/lay/lay/doc/images/drc_and3.png and b/src/lay/lay/doc/images/drc_and3.png differ diff --git a/src/lay/lay/doc/images/drc_centers1.png b/src/lay/lay/doc/images/drc_centers1.png index 4237e55f9..5f5b07296 100644 Binary files a/src/lay/lay/doc/images/drc_centers1.png and b/src/lay/lay/doc/images/drc_centers1.png differ diff --git a/src/lay/lay/doc/images/drc_centers2.png b/src/lay/lay/doc/images/drc_centers2.png index 07d6244de..65e267b7e 100644 Binary files a/src/lay/lay/doc/images/drc_centers2.png and b/src/lay/lay/doc/images/drc_centers2.png differ diff --git a/src/lay/lay/doc/images/drc_corners1.png b/src/lay/lay/doc/images/drc_corners1.png index 52a4e8f4d..461054bdf 100644 Binary files a/src/lay/lay/doc/images/drc_corners1.png and b/src/lay/lay/doc/images/drc_corners1.png differ diff --git a/src/lay/lay/doc/images/drc_corners2.png b/src/lay/lay/doc/images/drc_corners2.png index bad8c9dcb..24c48a316 100644 Binary files a/src/lay/lay/doc/images/drc_corners2.png and b/src/lay/lay/doc/images/drc_corners2.png differ diff --git a/src/lay/lay/doc/images/drc_corners3.png b/src/lay/lay/doc/images/drc_corners3.png index 061cdf486..ed6f97696 100644 Binary files a/src/lay/lay/doc/images/drc_corners3.png and b/src/lay/lay/doc/images/drc_corners3.png differ diff --git a/src/lay/lay/doc/images/drc_enc1.png b/src/lay/lay/doc/images/drc_enc1.png index cbf117d95..d4b448284 100644 Binary files a/src/lay/lay/doc/images/drc_enc1.png and b/src/lay/lay/doc/images/drc_enc1.png differ diff --git a/src/lay/lay/doc/images/drc_enc2.png b/src/lay/lay/doc/images/drc_enc2.png index 7b00902c5..27a38ac48 100644 Binary files a/src/lay/lay/doc/images/drc_enc2.png and b/src/lay/lay/doc/images/drc_enc2.png differ diff --git a/src/lay/lay/doc/images/drc_end_segments1.png b/src/lay/lay/doc/images/drc_end_segments1.png index bfac5785d..e146be063 100644 Binary files a/src/lay/lay/doc/images/drc_end_segments1.png and b/src/lay/lay/doc/images/drc_end_segments1.png differ diff --git a/src/lay/lay/doc/images/drc_end_segments2.png b/src/lay/lay/doc/images/drc_end_segments2.png index f3e0abb21..2bbf6f78b 100644 Binary files a/src/lay/lay/doc/images/drc_end_segments2.png and b/src/lay/lay/doc/images/drc_end_segments2.png differ diff --git a/src/lay/lay/doc/images/drc_extended1.png b/src/lay/lay/doc/images/drc_extended1.png index c919d576f..04ae3e835 100644 Binary files a/src/lay/lay/doc/images/drc_extended1.png and b/src/lay/lay/doc/images/drc_extended1.png differ diff --git a/src/lay/lay/doc/images/drc_extended2.png b/src/lay/lay/doc/images/drc_extended2.png index 1bb227e9a..287ad4aa7 100644 Binary files a/src/lay/lay/doc/images/drc_extended2.png and b/src/lay/lay/doc/images/drc_extended2.png differ diff --git a/src/lay/lay/doc/images/drc_extended3.png b/src/lay/lay/doc/images/drc_extended3.png index 6f3983891..185d601d0 100644 Binary files a/src/lay/lay/doc/images/drc_extended3.png and b/src/lay/lay/doc/images/drc_extended3.png differ diff --git a/src/lay/lay/doc/images/drc_extended4.png b/src/lay/lay/doc/images/drc_extended4.png index bfe992ca1..6e24afc48 100644 Binary files a/src/lay/lay/doc/images/drc_extended4.png and b/src/lay/lay/doc/images/drc_extended4.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs1.png b/src/lay/lay/doc/images/drc_extent_refs1.png index 072bdc417..2ab241463 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs1.png and b/src/lay/lay/doc/images/drc_extent_refs1.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs10.png b/src/lay/lay/doc/images/drc_extent_refs10.png index 517188045..81a5603fe 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs10.png and b/src/lay/lay/doc/images/drc_extent_refs10.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs11.png b/src/lay/lay/doc/images/drc_extent_refs11.png index 1fd5f3fed..9b3618059 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs11.png and b/src/lay/lay/doc/images/drc_extent_refs11.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs12.png b/src/lay/lay/doc/images/drc_extent_refs12.png index 18b60de9e..f84556d24 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs12.png and b/src/lay/lay/doc/images/drc_extent_refs12.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs13.png b/src/lay/lay/doc/images/drc_extent_refs13.png index 20009cf24..d1ebc5eaa 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs13.png and b/src/lay/lay/doc/images/drc_extent_refs13.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs20.png b/src/lay/lay/doc/images/drc_extent_refs20.png index a96e78264..170aa1b52 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs20.png and b/src/lay/lay/doc/images/drc_extent_refs20.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs21.png b/src/lay/lay/doc/images/drc_extent_refs21.png index 98fb1e73b..26efee149 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs21.png and b/src/lay/lay/doc/images/drc_extent_refs21.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs22.png b/src/lay/lay/doc/images/drc_extent_refs22.png index f91892d44..54581748d 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs22.png and b/src/lay/lay/doc/images/drc_extent_refs22.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs23.png b/src/lay/lay/doc/images/drc_extent_refs23.png index 13dfdf121..f3ad4bdca 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs23.png and b/src/lay/lay/doc/images/drc_extent_refs23.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs24.png b/src/lay/lay/doc/images/drc_extent_refs24.png index e6432eee9..673df6948 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs24.png and b/src/lay/lay/doc/images/drc_extent_refs24.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs25.png b/src/lay/lay/doc/images/drc_extent_refs25.png index e5222b8f1..f3a0db1a2 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs25.png and b/src/lay/lay/doc/images/drc_extent_refs25.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs26.png b/src/lay/lay/doc/images/drc_extent_refs26.png index dbe8ed39a..d855fb789 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs26.png and b/src/lay/lay/doc/images/drc_extent_refs26.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs27.png b/src/lay/lay/doc/images/drc_extent_refs27.png index 6d7a1a4fe..73b9597c0 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs27.png and b/src/lay/lay/doc/images/drc_extent_refs27.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs30.png b/src/lay/lay/doc/images/drc_extent_refs30.png index 2890ce143..c8ac61634 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs30.png and b/src/lay/lay/doc/images/drc_extent_refs30.png differ diff --git a/src/lay/lay/doc/images/drc_extent_refs31.png b/src/lay/lay/doc/images/drc_extent_refs31.png index e3c48d877..755dd2922 100644 Binary files a/src/lay/lay/doc/images/drc_extent_refs31.png and b/src/lay/lay/doc/images/drc_extent_refs31.png differ diff --git a/src/lay/lay/doc/images/drc_extents1.png b/src/lay/lay/doc/images/drc_extents1.png index fba7447aa..edc3fa060 100644 Binary files a/src/lay/lay/doc/images/drc_extents1.png and b/src/lay/lay/doc/images/drc_extents1.png differ diff --git a/src/lay/lay/doc/images/drc_extents2.png b/src/lay/lay/doc/images/drc_extents2.png index e42d9e5d3..bb14947ce 100644 Binary files a/src/lay/lay/doc/images/drc_extents2.png and b/src/lay/lay/doc/images/drc_extents2.png differ diff --git a/src/lay/lay/doc/images/drc_holes.png b/src/lay/lay/doc/images/drc_holes.png index e3f08ddff..e245a0b88 100644 Binary files a/src/lay/lay/doc/images/drc_holes.png and b/src/lay/lay/doc/images/drc_holes.png differ diff --git a/src/lay/lay/doc/images/drc_hulls.png b/src/lay/lay/doc/images/drc_hulls.png index cb2e7d726..65ef90930 100644 Binary files a/src/lay/lay/doc/images/drc_hulls.png and b/src/lay/lay/doc/images/drc_hulls.png differ diff --git a/src/lay/lay/doc/images/drc_in.png b/src/lay/lay/doc/images/drc_in.png index 958166227..75ba7043b 100644 Binary files a/src/lay/lay/doc/images/drc_in.png and b/src/lay/lay/doc/images/drc_in.png differ diff --git a/src/lay/lay/doc/images/drc_inside.png b/src/lay/lay/doc/images/drc_inside.png index f7066b7ae..3e8e74b0f 100644 Binary files a/src/lay/lay/doc/images/drc_inside.png and b/src/lay/lay/doc/images/drc_inside.png differ diff --git a/src/lay/lay/doc/images/drc_inside_part.png b/src/lay/lay/doc/images/drc_inside_part.png index dcd45b3bc..eb827a8b1 100644 Binary files a/src/lay/lay/doc/images/drc_inside_part.png and b/src/lay/lay/doc/images/drc_inside_part.png differ diff --git a/src/lay/lay/doc/images/drc_interacting.png b/src/lay/lay/doc/images/drc_interacting.png index 78e8bc476..96de49249 100644 Binary files a/src/lay/lay/doc/images/drc_interacting.png and b/src/lay/lay/doc/images/drc_interacting.png differ diff --git a/src/lay/lay/doc/images/drc_join1.png b/src/lay/lay/doc/images/drc_join1.png index f2a722e8d..74c2b4354 100644 Binary files a/src/lay/lay/doc/images/drc_join1.png and b/src/lay/lay/doc/images/drc_join1.png differ diff --git a/src/lay/lay/doc/images/drc_join2.png b/src/lay/lay/doc/images/drc_join2.png index 6f25d45dc..c6c751efe 100644 Binary files a/src/lay/lay/doc/images/drc_join2.png and b/src/lay/lay/doc/images/drc_join2.png differ diff --git a/src/lay/lay/doc/images/drc_merged1.png b/src/lay/lay/doc/images/drc_merged1.png index 1005e3d30..2f02b2930 100644 Binary files a/src/lay/lay/doc/images/drc_merged1.png and b/src/lay/lay/doc/images/drc_merged1.png differ diff --git a/src/lay/lay/doc/images/drc_merged2.png b/src/lay/lay/doc/images/drc_merged2.png index e60fb3060..76ea5368e 100644 Binary files a/src/lay/lay/doc/images/drc_merged2.png and b/src/lay/lay/doc/images/drc_merged2.png differ diff --git a/src/lay/lay/doc/images/drc_merged3.png b/src/lay/lay/doc/images/drc_merged3.png index 5e2590ecb..2f7ead9dd 100644 Binary files a/src/lay/lay/doc/images/drc_merged3.png and b/src/lay/lay/doc/images/drc_merged3.png differ diff --git a/src/lay/lay/doc/images/drc_merged4.png b/src/lay/lay/doc/images/drc_merged4.png index 8a0207a0c..0dc6f5580 100644 Binary files a/src/lay/lay/doc/images/drc_merged4.png and b/src/lay/lay/doc/images/drc_merged4.png differ diff --git a/src/lay/lay/doc/images/drc_middle1.png b/src/lay/lay/doc/images/drc_middle1.png index 7ac5afbd7..4fddc996b 100644 Binary files a/src/lay/lay/doc/images/drc_middle1.png and b/src/lay/lay/doc/images/drc_middle1.png differ diff --git a/src/lay/lay/doc/images/drc_moved1.png b/src/lay/lay/doc/images/drc_moved1.png index 7536a9088..649de520a 100644 Binary files a/src/lay/lay/doc/images/drc_moved1.png and b/src/lay/lay/doc/images/drc_moved1.png differ diff --git a/src/lay/lay/doc/images/drc_not1.png b/src/lay/lay/doc/images/drc_not1.png index 3f6bd28d1..c62d8a9d3 100644 Binary files a/src/lay/lay/doc/images/drc_not1.png and b/src/lay/lay/doc/images/drc_not1.png differ diff --git a/src/lay/lay/doc/images/drc_not2.png b/src/lay/lay/doc/images/drc_not2.png index ae43ae1a5..c501c3ada 100644 Binary files a/src/lay/lay/doc/images/drc_not2.png and b/src/lay/lay/doc/images/drc_not2.png differ diff --git a/src/lay/lay/doc/images/drc_not3.png b/src/lay/lay/doc/images/drc_not3.png index 6b4ef3e90..51b96ba4f 100644 Binary files a/src/lay/lay/doc/images/drc_not3.png and b/src/lay/lay/doc/images/drc_not3.png differ diff --git a/src/lay/lay/doc/images/drc_not_in.png b/src/lay/lay/doc/images/drc_not_in.png index d1829a7be..c3e7986b3 100644 Binary files a/src/lay/lay/doc/images/drc_not_in.png and b/src/lay/lay/doc/images/drc_not_in.png differ diff --git a/src/lay/lay/doc/images/drc_not_inside.png b/src/lay/lay/doc/images/drc_not_inside.png index ed735fdf2..aa4c880eb 100644 Binary files a/src/lay/lay/doc/images/drc_not_inside.png and b/src/lay/lay/doc/images/drc_not_inside.png differ diff --git a/src/lay/lay/doc/images/drc_not_interacting.png b/src/lay/lay/doc/images/drc_not_interacting.png index 97ee23413..27a48eb06 100644 Binary files a/src/lay/lay/doc/images/drc_not_interacting.png and b/src/lay/lay/doc/images/drc_not_interacting.png differ diff --git a/src/lay/lay/doc/images/drc_not_outside.png b/src/lay/lay/doc/images/drc_not_outside.png index d46555108..e6ede552a 100644 Binary files a/src/lay/lay/doc/images/drc_not_outside.png and b/src/lay/lay/doc/images/drc_not_outside.png differ diff --git a/src/lay/lay/doc/images/drc_not_overlapping.png b/src/lay/lay/doc/images/drc_not_overlapping.png index 95b70cf05..89f571221 100644 Binary files a/src/lay/lay/doc/images/drc_not_overlapping.png and b/src/lay/lay/doc/images/drc_not_overlapping.png differ diff --git a/src/lay/lay/doc/images/drc_or1.png b/src/lay/lay/doc/images/drc_or1.png index 4a3f4fe30..78386d1ff 100644 Binary files a/src/lay/lay/doc/images/drc_or1.png and b/src/lay/lay/doc/images/drc_or1.png differ diff --git a/src/lay/lay/doc/images/drc_or2.png b/src/lay/lay/doc/images/drc_or2.png index 58d0456dc..ae2974fa0 100644 Binary files a/src/lay/lay/doc/images/drc_or2.png and b/src/lay/lay/doc/images/drc_or2.png differ diff --git a/src/lay/lay/doc/images/drc_outside.png b/src/lay/lay/doc/images/drc_outside.png index 15aa2a81e..190b2537c 100644 Binary files a/src/lay/lay/doc/images/drc_outside.png and b/src/lay/lay/doc/images/drc_outside.png differ diff --git a/src/lay/lay/doc/images/drc_outside_part.png b/src/lay/lay/doc/images/drc_outside_part.png index efe5a56a2..16789b517 100644 Binary files a/src/lay/lay/doc/images/drc_outside_part.png and b/src/lay/lay/doc/images/drc_outside_part.png differ diff --git a/src/lay/lay/doc/images/drc_overlap1.png b/src/lay/lay/doc/images/drc_overlap1.png index dcb26b5ae..75d18813d 100644 Binary files a/src/lay/lay/doc/images/drc_overlap1.png and b/src/lay/lay/doc/images/drc_overlap1.png differ diff --git a/src/lay/lay/doc/images/drc_overlap2.png b/src/lay/lay/doc/images/drc_overlap2.png index 337826a7e..06d17e418 100644 Binary files a/src/lay/lay/doc/images/drc_overlap2.png and b/src/lay/lay/doc/images/drc_overlap2.png differ diff --git a/src/lay/lay/doc/images/drc_overlapping.png b/src/lay/lay/doc/images/drc_overlapping.png index c645a190f..2afb3ba85 100644 Binary files a/src/lay/lay/doc/images/drc_overlapping.png and b/src/lay/lay/doc/images/drc_overlapping.png differ diff --git a/src/lay/lay/doc/images/drc_raw1.png b/src/lay/lay/doc/images/drc_raw1.png index cf881ed93..fa2b1e6fd 100644 Binary files a/src/lay/lay/doc/images/drc_raw1.png and b/src/lay/lay/doc/images/drc_raw1.png differ diff --git a/src/lay/lay/doc/images/drc_raw2.png b/src/lay/lay/doc/images/drc_raw2.png index d0730e934..6bc582de5 100644 Binary files a/src/lay/lay/doc/images/drc_raw2.png and b/src/lay/lay/doc/images/drc_raw2.png differ diff --git a/src/lay/lay/doc/images/drc_raw3.png b/src/lay/lay/doc/images/drc_raw3.png index ba85651f4..0288c1784 100644 Binary files a/src/lay/lay/doc/images/drc_raw3.png and b/src/lay/lay/doc/images/drc_raw3.png differ diff --git a/src/lay/lay/doc/images/drc_rotated1.png b/src/lay/lay/doc/images/drc_rotated1.png index ab74db467..02011ed60 100644 Binary files a/src/lay/lay/doc/images/drc_rotated1.png and b/src/lay/lay/doc/images/drc_rotated1.png differ diff --git a/src/lay/lay/doc/images/drc_rounded_corners.png b/src/lay/lay/doc/images/drc_rounded_corners.png index 02e694e19..c77f0da8b 100644 Binary files a/src/lay/lay/doc/images/drc_rounded_corners.png and b/src/lay/lay/doc/images/drc_rounded_corners.png differ diff --git a/src/lay/lay/doc/images/drc_scaled1.png b/src/lay/lay/doc/images/drc_scaled1.png index 5e9030c59..32c75761e 100644 Binary files a/src/lay/lay/doc/images/drc_scaled1.png and b/src/lay/lay/doc/images/drc_scaled1.png differ diff --git a/src/lay/lay/doc/images/drc_separation1.png b/src/lay/lay/doc/images/drc_separation1.png index b958deeb1..1204d1d8a 100644 Binary files a/src/lay/lay/doc/images/drc_separation1.png and b/src/lay/lay/doc/images/drc_separation1.png differ diff --git a/src/lay/lay/doc/images/drc_sized1.png b/src/lay/lay/doc/images/drc_sized1.png index ec34fef66..c4a9d56a2 100644 Binary files a/src/lay/lay/doc/images/drc_sized1.png and b/src/lay/lay/doc/images/drc_sized1.png differ diff --git a/src/lay/lay/doc/images/drc_sized2.png b/src/lay/lay/doc/images/drc_sized2.png index 2949de1b1..af9090297 100644 Binary files a/src/lay/lay/doc/images/drc_sized2.png and b/src/lay/lay/doc/images/drc_sized2.png differ diff --git a/src/lay/lay/doc/images/drc_sized3.png b/src/lay/lay/doc/images/drc_sized3.png index 61a4b8ca7..d5a1f1ad6 100644 Binary files a/src/lay/lay/doc/images/drc_sized3.png and b/src/lay/lay/doc/images/drc_sized3.png differ diff --git a/src/lay/lay/doc/images/drc_sized4.png b/src/lay/lay/doc/images/drc_sized4.png index e96e99747..d178ed236 100644 Binary files a/src/lay/lay/doc/images/drc_sized4.png and b/src/lay/lay/doc/images/drc_sized4.png differ diff --git a/src/lay/lay/doc/images/drc_sized5.png b/src/lay/lay/doc/images/drc_sized5.png index 89d97d50e..556ffb531 100644 Binary files a/src/lay/lay/doc/images/drc_sized5.png and b/src/lay/lay/doc/images/drc_sized5.png differ diff --git a/src/lay/lay/doc/images/drc_sized6.png b/src/lay/lay/doc/images/drc_sized6.png index f045511c0..c81dbdcd2 100644 Binary files a/src/lay/lay/doc/images/drc_sized6.png and b/src/lay/lay/doc/images/drc_sized6.png differ diff --git a/src/lay/lay/doc/images/drc_space1.png b/src/lay/lay/doc/images/drc_space1.png index 8f91261fb..81b377a1a 100644 Binary files a/src/lay/lay/doc/images/drc_space1.png and b/src/lay/lay/doc/images/drc_space1.png differ diff --git a/src/lay/lay/doc/images/drc_space2.png b/src/lay/lay/doc/images/drc_space2.png index df7dd0b40..c4cc02b5b 100644 Binary files a/src/lay/lay/doc/images/drc_space2.png and b/src/lay/lay/doc/images/drc_space2.png differ diff --git a/src/lay/lay/doc/images/drc_space3.png b/src/lay/lay/doc/images/drc_space3.png index eabee35ee..db0b50898 100644 Binary files a/src/lay/lay/doc/images/drc_space3.png and b/src/lay/lay/doc/images/drc_space3.png differ diff --git a/src/lay/lay/doc/images/drc_start_segments1.png b/src/lay/lay/doc/images/drc_start_segments1.png index 3644f360d..a6b45ecab 100644 Binary files a/src/lay/lay/doc/images/drc_start_segments1.png and b/src/lay/lay/doc/images/drc_start_segments1.png differ diff --git a/src/lay/lay/doc/images/drc_start_segments2.png b/src/lay/lay/doc/images/drc_start_segments2.png index 800ec51dc..af8dfa514 100644 Binary files a/src/lay/lay/doc/images/drc_start_segments2.png and b/src/lay/lay/doc/images/drc_start_segments2.png differ diff --git a/src/lay/lay/doc/images/drc_transformed1.png b/src/lay/lay/doc/images/drc_transformed1.png index ee33932d0..d232cbd6e 100644 Binary files a/src/lay/lay/doc/images/drc_transformed1.png and b/src/lay/lay/doc/images/drc_transformed1.png differ diff --git a/src/lay/lay/doc/images/drc_width1.png b/src/lay/lay/doc/images/drc_width1.png index b14ce9ddf..8fa847a50 100644 Binary files a/src/lay/lay/doc/images/drc_width1.png and b/src/lay/lay/doc/images/drc_width1.png differ diff --git a/src/lay/lay/doc/images/drc_width2.png b/src/lay/lay/doc/images/drc_width2.png index c9503e182..1448ababb 100644 Binary files a/src/lay/lay/doc/images/drc_width2.png and b/src/lay/lay/doc/images/drc_width2.png differ diff --git a/src/lay/lay/doc/images/drc_width3.png b/src/lay/lay/doc/images/drc_width3.png index 23287c77d..2b4af24d9 100644 Binary files a/src/lay/lay/doc/images/drc_width3.png and b/src/lay/lay/doc/images/drc_width3.png differ diff --git a/src/lay/lay/doc/images/drc_width4.png b/src/lay/lay/doc/images/drc_width4.png index 1e706274f..727007a4b 100644 Binary files a/src/lay/lay/doc/images/drc_width4.png and b/src/lay/lay/doc/images/drc_width4.png differ diff --git a/src/lay/lay/doc/images/drc_with_angle1.png b/src/lay/lay/doc/images/drc_with_angle1.png index 22dbcf75e..4c9ba1f72 100644 Binary files a/src/lay/lay/doc/images/drc_with_angle1.png and b/src/lay/lay/doc/images/drc_with_angle1.png differ diff --git a/src/lay/lay/doc/images/drc_with_angle2.png b/src/lay/lay/doc/images/drc_with_angle2.png index 8ccd9ec0c..5fe220c01 100644 Binary files a/src/lay/lay/doc/images/drc_with_angle2.png and b/src/lay/lay/doc/images/drc_with_angle2.png differ diff --git a/src/lay/lay/doc/images/drc_with_angle3.png b/src/lay/lay/doc/images/drc_with_angle3.png index 26ed55013..1aa664498 100644 Binary files a/src/lay/lay/doc/images/drc_with_angle3.png and b/src/lay/lay/doc/images/drc_with_angle3.png differ diff --git a/src/lay/lay/doc/images/drc_with_angle4.png b/src/lay/lay/doc/images/drc_with_angle4.png index 9d938f550..6e5246d2a 100644 Binary files a/src/lay/lay/doc/images/drc_with_angle4.png and b/src/lay/lay/doc/images/drc_with_angle4.png differ diff --git a/src/lay/lay/doc/images/drc_xor1.png b/src/lay/lay/doc/images/drc_xor1.png index 16191d3ca..6a53ec961 100644 Binary files a/src/lay/lay/doc/images/drc_xor1.png and b/src/lay/lay/doc/images/drc_xor1.png differ diff --git a/src/lay/lay/doc/images/drc_xor2.png b/src/lay/lay/doc/images/drc_xor2.png index 2dd68bc8c..5b391cd3f 100644 Binary files a/src/lay/lay/doc/images/drc_xor2.png and b/src/lay/lay/doc/images/drc_xor2.png differ diff --git a/src/lay/lay/doc/manual/lvs_symm_nodes.png b/src/lay/lay/doc/manual/lvs_symm_nodes.png new file mode 100644 index 000000000..faf82bdf3 Binary files /dev/null and b/src/lay/lay/doc/manual/lvs_symm_nodes.png differ diff --git a/src/lay/lay/doc/manual/lvs_tweaks.xml b/src/lay/lay/doc/manual/lvs_tweaks.xml index f0300a18d..2e631f677 100644 --- a/src/lay/lay/doc/manual/lvs_tweaks.xml +++ b/src/lay/lay/doc/manual/lvs_tweaks.xml @@ -175,6 +175,52 @@ schematic.blank_circuit("CIRCUIT_NAME") sets a flag () which prevents purging of abstract circuits.

+

Joining of symmetric nodes

+ +

+ Sometimes it is possible to omit connections in the layout because these + will not carry any current. This might simplify the layout and allow denser + packing, but finally there is a mismatch between schematic and layout. + In general, connections can be omitted if they would connect symmetric + nodes. When symmetric nodes are swapped, the circuit will not change. + Hence they will always carry the same potential (at least in theory) + and a connection between them will not carry any current. So it can + be omitted. +

+ +

+ The following picture describes such a situation known as "split gate configuration". + In this case, the N1 and N2 are identical: swapping them will not change the circuit's + topology. Hence, they will carry the same potential and the red connection is not required physically. + But without such a connection, the parallel transistors (top pair and bottom pair) will not be recognized + as parallel and the pairs will not be joined into one each: +

+ +

+ +

+ +

+ KLayout provides a feature which will add such connections after extraction + of the netlist: +

+ +
join_symmetric_nets("NAND2")
+ +

+ This function will analyze the circuit "NAND2" in the extracted netlist and connect all symmetric + nodes within it. If this function is called before "combine_devices" (e.g. through + "netlist.simplify"), this connection is already present then and parallel devices + will be recognized and combined. +

+ +

+ The argument to "join_symmetric_nets" is a glob-style pattern. "*" will analyze and + modify all circuits, but at the price of potentially introducing unwanted connections. + Hence the recommendation is to use this feature on circuits which are known to + need it. +

+

Purging (elimination of redundancy)

diff --git a/src/lay/lay/layApplication.cc b/src/lay/lay/layApplication.cc index 0d5024e9f..4b8004b8c 100644 --- a/src/lay/lay/layApplication.cc +++ b/src/lay/lay/layApplication.cc @@ -1283,14 +1283,6 @@ ApplicationBase::get_config_names () const return names; } -bool -ApplicationBase::special_app_flag (const std::string &name) -{ - // TODO: some more elaborate scheme? - const char *env = getenv (("KLAYOUT_" + name).c_str ()); - return (env && *env); -} - // -------------------------------------------------------------------------------- // GuiApplication implementation diff --git a/src/lay/lay/layApplication.h b/src/lay/lay/layApplication.h index 8ef31e2d5..5bd45fa6c 100644 --- a/src/lay/lay/layApplication.h +++ b/src/lay/lay/layApplication.h @@ -198,16 +198,6 @@ public: */ std::vector get_config_names () const; - /** - * @brief Gets a value indicating whether the give special application flag is set - * - * Special application flags are ways to introduce debug or flags for special - * use cases. Such flags have a name and currently are controlled externally by - * an environment variable called "KLAYOUT_x" where x is the name. If that - * variable is set and the value is non-empty, the flag is regarded set. - */ - bool special_app_flag (const std::string &name); - /** * @brief Return a reference to the Ruby interpreter */ diff --git a/src/lay/lay/layHelpResources.qrc b/src/lay/lay/layHelpResources.qrc index 34dbc7efd..d6ba81d93 100644 --- a/src/lay/lay/layHelpResources.qrc +++ b/src/lay/lay/layHelpResources.qrc @@ -181,6 +181,7 @@ doc/manual/lvs_io.xml doc/manual/lvs_compare.xml doc/manual/lvs_tweaks.xml + doc/manual/lvs_symm_nodes.png doc/manual/lvs_connect.xml doc/manual/inv.png doc/manual/inv_no_transistors.png diff --git a/src/lay/lay/laySystemPaths.cc b/src/lay/lay/laySystemPaths.cc index 719c41090..708f1ed45 100644 --- a/src/lay/lay/laySystemPaths.cc +++ b/src/lay/lay/laySystemPaths.cc @@ -24,6 +24,7 @@ #include "laySystemPaths.h" #include "tlFileUtils.h" #include "tlString.h" +#include "tlEnv.h" #include #include @@ -31,32 +32,17 @@ #ifdef _WIN32 # include -#elif __APPLE__ -# include -# include -#else -# include #endif -#include - namespace lay { std::string get_appdata_path () { -#ifdef _WIN32 - wchar_t *env = _wgetenv (L"KLAYOUT_HOME"); - if (env) { - return tl::to_string (QString ((const QChar *) env)); + if (tl::has_env ("KLAYOUT_HOME")) { + return tl::get_env ("KLAYOUT_HOME"); } -#else - char *env = getenv ("KLAYOUT_HOME"); - if (env) { - return (tl::system_to_string (env)); - } -#endif QDir appdata_dir = QDir::homePath (); QString appdata_folder; @@ -138,23 +124,14 @@ get_klayout_path () // generate the klayout path: the first component is always the appdata path klayout_path.push_back (get_appdata_path ()); -#ifdef _WIN32 - wchar_t *env = _wgetenv (L"KLAYOUT_PATH"); - if (env) { - split_path (tl::to_string (QString ((const QChar *) env)), klayout_path); + + std::string env = tl::get_env ("KLAYOUT_PATH"); + if (! env.empty ()) { + split_path (env, klayout_path); } else { get_other_system_paths (klayout_path); klayout_path.push_back (tl::get_inst_path ()); } -#else - char *env = getenv ("KLAYOUT_PATH"); - if (env) { - split_path (tl::system_to_string (env), klayout_path); - } else { - get_other_system_paths (klayout_path); - klayout_path.push_back (tl::get_inst_path ()); - } -#endif return klayout_path; @@ -164,23 +141,7 @@ get_klayout_path () std::string salt_mine_url () { - const std::string default_url ("http://sami.klayout.org/repository.xml"); - -#ifdef _WIN32 - wchar_t *env = _wgetenv (L"KLAYOUT_SALT_MINE"); - if (env) { - return tl::to_string (QString ((const QChar *) env)); - } else { - return default_url; - } -#else - char *env = getenv ("KLAYOUT_SALT_MINE"); - if (env) { - return (tl::system_to_string (env)); - } else { - return default_url; - } -#endif + return tl::get_env ("KLAYOUT_SALT_MINE", "http://sami.klayout.org/repository.xml"); } } diff --git a/src/lvs/lvs/built-in-macros/_lvs_engine.rb b/src/lvs/lvs/built-in-macros/_lvs_engine.rb index 8186eb2eb..ebb18df60 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_engine.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_engine.rb @@ -94,6 +94,12 @@ module LVS # @synopsis compare # See \Netter#compare for a description of that function. + # %LVS% + # @name join_symmetric_nets + # @brief Joins symmetric nets of selected circuits on the extracted netlist + # @synopsis join_symmetric_nets(circuit_filter) + # See \Netter#join_symmetric_nets for a description of that function. + # %LVS% # @name align # @brief Aligns the extracted netlist vs. the schematic by flattening circuits where required @@ -149,7 +155,7 @@ module LVS # @synopsis max_depth(n) # See \Netter#max_depth for a description of that function. - %w(schematic compare align same_nets same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity).each do |f| + %w(schematic compare join_symmetric_nets align same_nets same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity).each do |f| eval <<"CODE" def #{f}(*args) _netter.#{f}(*args) diff --git a/src/lvs/lvs/built-in-macros/_lvs_netter.rb b/src/lvs/lvs/built-in-macros/_lvs_netter.rb index ca5fcf5fa..1d0bd03be 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_netter.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_netter.rb @@ -172,6 +172,39 @@ module LVS end + # %LVS% + # @name join_symmetric_nets + # @brief Joins symmetric nets of selected circuits on the extracted netlist + # @synopsis join_symmetric_nets(circuit_filter) + # Nets are symmetrical if swapping them would not modify the circuit. + # Hence they will carry the same potential and can be connected (joined). + # This will simplify the circuit and can be applied before device combination + # (e.g. through "netlist.simplify") to render a schematic-equivalent netlist in some + # cases where symmetric nodes are split (i.e. "split gate" configuration). + # + # This method operates on the extracted netlist (layout). The circuit filter + # specifies the circuits to which to apply this operation. The filter is a + # glob-style pattern. Using "*" for all circuits is possible, but it's + # discouraged currenty until the reliability of the symmetry detection + # algorithm is established. Currently it is recommended to apply it only to + # those circuits for which this feature is required. + # + # For the symmetry detection, the specified constraints (e.g. tolerances, + # device filters etc.) apply. + + def join_symmetric_nets(circuit_pattern) + + circuit_pattern.is_a?(String) || raise("Circuit pattern argument of 'join_symmetric_nets' must be a string") + + comparer = self._comparer + + netlist || raise("No netlist present (not extracted?)") + netlist.circuits_by_name(circuit_pattern).each do |c| + comparer.join_symmetric_nets(c) + end + + end + def _comparer comparer = RBA::NetlistComparer::new diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index 7e08174c3..df9709da6 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -177,3 +177,8 @@ TEST(20_double_height2_inv) run_test (_this, "double_height2", "double_height2_inv.gds"); } +TEST(21_split_gate) +{ + run_test (_this, "nand2_split_gate", "nand2_split_gate.oas"); +} + diff --git a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc index ccb55afed..e4597edce 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc +++ b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc @@ -35,6 +35,7 @@ #include "tlTimer.h" #include "tlProgress.h" #include "tlThreadedWorkers.h" +#include "tlEnv.h" #include "tlExceptions.h" #include "tlMath.h" #include "layCellView.h" @@ -1085,7 +1086,7 @@ XORToolDialog::run_xor () bool summarize = mp_ui->summarize_cb->isChecked (); // TODO: make this a user interface feature later - bool process_el = lay::ApplicationBase::instance ()->special_app_flag ("ALWAYS_DO_XOR"); + bool process_el = tl::app_flag ("always-do-xor"); int cv_index_a = mp_ui->layouta->current_cv_index (); int cv_index_b = mp_ui->layoutb->current_cv_index (); diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index c63c2ec55..7ab83679f 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -46,7 +46,8 @@ SOURCES = \ tlList.cc \ tlEquivalenceClusters.cc \ tlUniqueName.cc \ - tlRecipe.cc + tlRecipe.cc \ + tlEnv.cc HEADERS = \ tlAlgorithm.h \ @@ -103,7 +104,8 @@ HEADERS = \ tlEquivalenceClusters.h \ tlUniqueName.h \ tlRecipe.h \ - tlSelect.h + tlSelect.h \ + tlEnv.h equals(HAVE_CURL, "1") { diff --git a/src/tl/tl/tlEnv.cc b/src/tl/tl/tlEnv.cc new file mode 100644 index 000000000..1c76b99c6 --- /dev/null +++ b/src/tl/tl/tlEnv.cc @@ -0,0 +1,85 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "tlEnv.h" +#include "tlString.h" + +#include + +#ifdef _WIN32 +# include +#elif __APPLE__ +# include +# include +#else +# include +#endif + +namespace tl +{ + +std::string get_env (const std::string &name, const std::string &def_value) +{ +#ifdef _WIN32 + std::wstring wname = tl::to_wstring (name); + wchar_t *env = _wgetenv (wname.c_str ()); + if (env) { + return tl::to_string (std::wstring (env)); + } else { + return def_value; + } +#else + char *env = getenv (name.c_str ()); + if (env) { + return tl::system_to_string (env); + } else { + return def_value; + } +#endif +} + +bool has_env (const std::string &name) +{ +#ifdef _WIN32 + std::wstring wname = tl::to_wstring (name); + wchar_t *env = _wgetenv (wname.c_str ()); + return env != 0; +#else + char *env = getenv (name.c_str ()); + return env != 0; +#endif +} + +bool app_flag (const std::string &name) +{ + std::string env_name = std::string ("KLAYOUT_") + tl::replaced (tl::to_upper_case (name), "-", "_"); + + int v = 0; + std::string vs = get_env (env_name); + tl::Extractor ex (vs.c_str ()); + return ex.try_read (v) && v != 0; +} + +} // namespace tl + + diff --git a/src/tl/tl/tlEnv.h b/src/tl/tl/tlEnv.h new file mode 100644 index 000000000..898ec142c --- /dev/null +++ b/src/tl/tl/tlEnv.h @@ -0,0 +1,65 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_tlAppFlags +#define HDR_tlAppFlags + +#include "tlCommon.h" + +#include + +namespace tl +{ + +/** + * @brief Gets the value for the given environment variable + * + * If the environment variable is not set, the default value will be returned. + */ +std::string TL_PUBLIC get_env (const std::string &name, const std::string &def_value = std::string ()); + +/** + * @brief Gets the value if the given environment variable is set + */ +bool TL_PUBLIC has_env (const std::string &name); + +/** + * @brief Gets an application flag with the given name + * + * This feature is supposed to deliver special flags for debugging etc. + * By using a central access point for these settings, it will be easy to provide + * different implementations later. + * + * Currently, application flags are derived by checking whether a corresponding + * environment variable exists. Names like "a-b" are translated into "KLAYOUT_A_B" + * for the environment variable. + * + * If the corresponding variable exists and does not have a value of "0", true + * is returned from this function. + */ +bool TL_PUBLIC app_flag (const std::string &name); + +} + +#endif + diff --git a/src/tl/tl/tlExpression.cc b/src/tl/tl/tlExpression.cc index 1d26b061b..28cb07d69 100644 --- a/src/tl/tl/tlExpression.cc +++ b/src/tl/tl/tlExpression.cc @@ -26,6 +26,7 @@ #include "tlString.h" #include "tlGlobPattern.h" #include "tlFileUtils.h" +#include "tlEnv.h" #include #include @@ -35,7 +36,6 @@ #define _USE_MATH_DEFINES // for MSVC #include #include -#include #include // Suggestions for further functions: @@ -2800,9 +2800,9 @@ env_f (const ExpressionParserContext &context, tl::Variant &out, const std::vect throw EvalError (tl::to_string (tr ("'env' function expects exactly two arguments")), context); } - const char *env = getenv (vv [0].to_string ()); - if (env) { - out = env; + const char *vn = vv [0].to_string (); + if (tl::has_env (vn)) { + out = tl::get_env (vn); } else { out = tl::Variant (); } diff --git a/src/tl/tl/tlLog.cc b/src/tl/tl/tlLog.cc index 258e793a3..1ada55bf2 100644 --- a/src/tl/tl/tlLog.cc +++ b/src/tl/tl/tlLog.cc @@ -23,16 +23,12 @@ #include "tlLog.h" #include "tlString.h" +#include "tlEnv.h" #include #if !defined(_MSC_VER) # include #endif -#include - -#if defined(_WIN32) -# include -#endif namespace tl { @@ -42,21 +38,9 @@ namespace tl static int default_verbosity () { - const char *verbosity_str = 0; - -#if defined(_WIN32) - const wchar_t *verbosity_wstr = _wgetenv (L"KLAYOUT_VERBOSITY"); - std::string vs; - if (verbosity_wstr) { - vs = tl::to_string (std::wstring (verbosity_wstr)); - verbosity_str = vs.c_str (); - } -#else - verbosity_str = getenv ("KLAYOUT_VERBOSITY"); -#endif - int verbosity = 0; - if (verbosity_str) { + std::string verbosity_str = tl::get_env ("KLAYOUT_VERBOSITY"); + if (! verbosity_str.empty ()) { try { tl::from_string (verbosity_str, verbosity); } catch (...) { diff --git a/src/tl/tl/tlUnitTest.cc b/src/tl/tl/tlUnitTest.cc index 8a719b5fb..ecebd781f 100644 --- a/src/tl/tl/tlUnitTest.cc +++ b/src/tl/tl/tlUnitTest.cc @@ -24,6 +24,7 @@ #include "tlFileUtils.h" #include "tlTimer.h" #include "tlStream.h" +#include "tlEnv.h" #include @@ -85,8 +86,8 @@ void set_debug_mode (bool f) std::string testsrc () { - const char *ts = getenv ("TESTSRC"); - if (! ts) { + std::string ts = tl::get_env ("TESTSRC"); + if (ts.empty ()) { throw tl::Exception ("TESTSRC undefined"); } return ts; @@ -104,8 +105,8 @@ std::string testsrc_private () std::string testtmp () { // Ensures the test temp directory is present - const char *tt = getenv ("TESTTMP"); - if (! tt) { + std::string tt = tl::get_env ("TESTTMP"); + if (tt.empty ()) { throw tl::Exception ("TESTTMP undefined"); } return tt; @@ -145,6 +146,15 @@ CaptureChannel::CaptureChannel () tl::info.add (this, false); tl::error.add (this, false); tl::warn.add (this, false); + + // Because we don't want to capture logger messages, we switch verbosity to "silent" during capturing + m_saved_verbosity = tl::verbosity (); + tl::verbosity (0); +} + +CaptureChannel::~CaptureChannel () +{ + tl::verbosity (m_saved_verbosity); } void CaptureChannel::puts (const char *s) diff --git a/src/tl/tl/tlUnitTest.h b/src/tl/tl/tlUnitTest.h index c24fa9714..98f5eda02 100644 --- a/src/tl/tl/tlUnitTest.h +++ b/src/tl/tl/tlUnitTest.h @@ -191,6 +191,7 @@ class TL_PUBLIC CaptureChannel : public tl::Channel { public: CaptureChannel (); + ~CaptureChannel (); std::string captured_text () const { @@ -210,6 +211,7 @@ protected: private: std::ostringstream m_text; + int m_saved_verbosity; }; /** diff --git a/src/tl/unit_tests/tlExpression.cc b/src/tl/unit_tests/tlExpression.cc index 7bbb7dcc5..751a3e3a3 100644 --- a/src/tl/unit_tests/tlExpression.cc +++ b/src/tl/unit_tests/tlExpression.cc @@ -24,8 +24,8 @@ #include "tlExpression.h" #include "tlVariantUserClasses.h" #include "tlUnitTest.h" +#include "tlEnv.h" -#include #define _USE_MATH_DEFINES // for MSVC #include @@ -834,7 +834,7 @@ TEST(6) v = e.parse ("env('HJASK')").execute (); EXPECT_EQ (v.to_string (), std::string ("nil")); v = e.parse ("env('PATH')").execute (); - EXPECT_EQ (v.to_string (), std::string (getenv ("PATH"))); + EXPECT_EQ (v.to_string (), tl::get_env ("PATH")); v = e.parse ("absolute_path('./x.gds')").execute (); // EXPECT_EQ (v.to_string (), std::string ()); // not universal v = e.parse ("absolute_file_path('./x.gds')").execute (); diff --git a/testdata/algo/device_extract_au1_joined_nets.gds b/testdata/algo/device_extract_au1_joined_nets.gds new file mode 100644 index 000000000..ceda8361f Binary files /dev/null and b/testdata/algo/device_extract_au1_joined_nets.gds differ diff --git a/testdata/lvs/nand2_split_gate.cir b/testdata/lvs/nand2_split_gate.cir new file mode 100644 index 000000000..5c739a1a4 --- /dev/null +++ b/testdata/lvs/nand2_split_gate.cir @@ -0,0 +1,23 @@ +* Extracted by KLayout + +* cell NAND2_WITH_DIODES +* pin B +* pin A +* pin OUT +* pin VDD +* pin VSS +.SUBCKT NAND2_WITH_DIODES 1 2 4 5 6 +* net 1 B +* net 2 A +* net 4 OUT +* net 5 VDD +* net 6 VSS +* device instance $1 r0 *1 1.025,4.95 PMOS +M$1 5 1 4 5 PMOS L=0.25U W=1.5U AS=0.675P AD=0.375P PS=3.9U PD=2U +* device instance $2 r0 *1 1.775,4.95 PMOS +M$2 4 2 5 5 PMOS L=0.25U W=1.5U AS=0.375P AD=0.675P PS=2U PD=3.9U +* device instance $3 r0 *1 1.025,2 NMOS +M$3 6 1 3 6 NMOS L=0.25U W=1.8U AS=0.81P AD=0.45P PS=5.4U PD=2.8U +* device instance $4 r0 *1 1.775,2 NMOS +M$4 3 2 4 6 NMOS L=0.25U W=1.8U AS=0.45P AD=0.81P PS=2.8U PD=5.4U +.ENDS NAND2_WITH_DIODES diff --git a/testdata/lvs/nand2_split_gate.lvs b/testdata/lvs/nand2_split_gate.lvs new file mode 100644 index 000000000..1c4a795bf --- /dev/null +++ b/testdata/lvs/nand2_split_gate.lvs @@ -0,0 +1,84 @@ + +source($lvs_test_source, "NAND2_WITH_DIODES") + +report_lvs($lvs_test_target_lvsdb, true) + +target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout") + +schematic("nand2_split_gate_schematic.cir") + +deep + +# Reports generated + +# Drawing layers + +nwell = input(1, 0) +active = input(2, 0) +pplus = input(3, 0) +nplus = input(4, 0) +poly = input(5, 0) +contact = input(6, 0) +metal1 = input(7, 0) +metal1_lbl = labels(7, 1) +via1 = input(8, 0) +metal2 = input(9, 0) +metal2_lbl = labels(9, 1) + +# Bulk layer for terminal provisioning + +bulk = polygon_layer + +# Computed layers + +active_in_nwell = active & nwell +pactive = active_in_nwell & pplus +pgate = pactive & poly +psd = pactive - pgate +ntie = active_in_nwell & nplus + +active_outside_nwell = active - nwell +nactive = active_outside_nwell & nplus +ngate = nactive & poly +nsd = nactive - ngate +ptie = active_outside_nwell & pplus + +# 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 +connect(psd, contact) +connect(nsd, contact) +connect(poly, contact) +connect(ntie, contact) +connect(nwell, ntie) +connect(ptie, contact) +connect(contact, metal1) +connect(metal1, metal1_lbl) # attaches labels +connect(metal1, via1) +connect(via1, metal2) +connect(metal2, metal2_lbl) # attaches labels + +# Global +connect_global(bulk, "SUBSTRATE") +connect_global(ptie, "SUBSTRATE") + +# Extract, simplify + +netlist +join_symmetric_nets("*") +netlist.simplify + +# Compare section + +compare + diff --git a/testdata/lvs/nand2_split_gate.lvsdb b/testdata/lvs/nand2_split_gate.lvsdb new file mode 100644 index 000000000..b37d55d89 --- /dev/null +++ b/testdata/lvs/nand2_split_gate.lvsdb @@ -0,0 +1,407 @@ +#%lvsdb-klayout + +# Layout +layout( + top(NAND2_WITH_DIODES) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l3 'NWELL (1/0)') + layer(l4 'POLY (5/0)') + layer(l8 'CONTACT (6/0)') + layer(l11 'METAL1 (7/0)') + layer(l12 'METAL1_LABEL (7/1)') + layer(l13 'VIA1 (8/0)') + layer(l14 'METAL2 (9/0)') + layer(l15 'METAL2_LABEL (9/1)') + layer(l7) + layer(l2) + layer(l9) + layer(l6) + layer(l10) + + # Mask layer connectivity + connect(l3 l3 l9) + connect(l4 l4 l8) + connect(l8 l4 l8 l11 l2 l9 l6 l10) + connect(l11 l8 l11 l12 l13) + connect(l12 l11 l12) + connect(l13 l11 l13 l14) + connect(l14 l13 l14 l15) + connect(l15 l14 l15) + connect(l7 l7) + connect(l2 l8 l2) + connect(l9 l3 l8 l9) + connect(l6 l8 l6) + connect(l10 l8 l10) + + # Global nets and connectivity + global(l7 SUBSTRATE) + global(l10 SUBSTRATE) + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$PMOS PMOS + terminal(S + rect(l2 (-575 -750) (450 1500)) + ) + terminal(G + rect(l4 (-125 -750) (250 1500)) + ) + terminal(D + rect(l2 (125 -750) (500 1500)) + ) + terminal(B + rect(l3 (-125 -750) (250 1500)) + ) + ) + device(D$PMOS$1 PMOS + terminal(S + rect(l2 (-625 -750) (500 1500)) + ) + terminal(G + rect(l4 (-125 -750) (250 1500)) + ) + terminal(D + rect(l2 (125 -750) (450 1500)) + ) + terminal(B + rect(l3 (-125 -750) (250 1500)) + ) + ) + device(D$NMOS NMOS + terminal(S + rect(l6 (-575 -450) (450 900)) + ) + terminal(G + rect(l4 (-125 -450) (250 900)) + ) + terminal(D + rect(l6 (125 -450) (500 900)) + ) + terminal(B + rect(l7 (-125 -450) (250 900)) + ) + ) + device(D$NMOS$1 NMOS + terminal(S + rect(l6 (-625 -450) (500 900)) + ) + terminal(G + rect(l4 (-125 -450) (250 900)) + ) + terminal(D + rect(l6 (125 -450) (450 900)) + ) + terminal(B + rect(l7 (-125 -450) (250 900)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(NAND2_WITH_DIODES + + # Circuit boundary + rect((0 0) (3750 6150)) + + # Nets with their geometries + net(1 name(B) + rect(l4 (350 2750) (550 400)) + rect(l4 (0 -2050) (250 3100)) + rect(l4 (-250 0) (250 1650)) + rect(l4 (-250 -5800) (250 1050)) + rect(l4 (-250 300) (250 1050)) + rect(l8 (-700 400) (200 200)) + rect(l11 (-300 -300) (400 400)) + rect(l12 (-201 -201) (2 2)) + ) + net(2 name(A) + rect(l4 (1900 3400) (550 400)) + rect(l4 (-800 -2700) (250 3100)) + rect(l4 (-250 0) (250 1650)) + rect(l4 (-250 -5800) (250 1050)) + rect(l4 (-250 300) (250 1050)) + rect(l8 (250 1050) (200 200)) + rect(l11 (-300 -300) (400 400)) + rect(l12 (-201 -201) (2 2)) + ) + net(3 + rect(l8 (1300 300) (200 200)) + rect(l8 (-200 -200) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (-200 -200) (200 200)) + rect(l8 (-200 650) (200 200)) + rect(l8 (-200 -200) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (-200 -200) (200 200)) + rect(l11 (-250 -2150) (300 900)) + rect(l11 (-300 -900) (300 850)) + rect(l11 (-300 500) (300 900)) + rect(l11 (-300 -900) (300 850)) + rect(l6 (-400 -2200) (500 900)) + rect(l6 (-500 450) (500 900)) + ) + net(4 name(OUT) + rect(l8 (2050 300) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (-200 650) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (-950 2000) (200 200)) + rect(l8 (-200 -200) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (-200 -200) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (-200 -200) (200 200)) + rect(l11 (500 -5350) (300 850)) + rect(l11 (-300 -50) (300 1950)) + rect(l11 (-300 -1400) (300 850)) + rect(l11 (-300 300) (450 400)) + rect(l11 (-1200 -300) (1050 300)) + rect(l11 (-1050 1150) (300 1400)) + rect(l11 (-300 -2700) (300 1950)) + rect(l12 (699 -2001) (2 2)) + rect(l2 (-1101 1299) (500 1500)) + rect(l6 (250 -5500) (450 900)) + rect(l6 (-450 450) (450 900)) + ) + net(5 name(VDD) + rect(l3 (0 2950) (3750 3200)) + rect(l8 (-3200 -1800) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (1300 -1200) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (700 -800) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l11 (-2650 -1200) (300 1600)) + rect(l11 (1200 -1600) (300 1600)) + rect(l11 (600 -1200) (300 1200)) + rect(l13 (-2650 -800) (200 200)) + rect(l13 (-200 300) (200 200)) + rect(l13 (1300 -700) (200 200)) + rect(l13 (-200 300) (200 200)) + rect(l13 (700 -700) (200 200)) + rect(l13 (-200 300) (200 200)) + rect(l14 (-3150 -850) (3750 1000)) + rect(l15 (-101 -851) (2 2)) + rect(l2 (-3201 -851) (450 1500)) + rect(l2 (1000 -1500) (450 1500)) + rect(l9 (400 -1200) (600 1200)) + ) + net(6 name(VSS) + rect(l8 (550 1650) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (-200 -2050) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l8 (2200 -550) (200 200)) + rect(l8 (-200 300) (200 200)) + rect(l11 (-2650 -50) (300 1350)) + rect(l11 (-300 -2400) (300 1050)) + rect(l11 (2100 -1050) (300 1200)) + rect(l13 (-2650 -1100) (200 200)) + rect(l13 (-200 300) (200 200)) + rect(l13 (2200 -700) (200 200)) + rect(l13 (-200 300) (200 200)) + rect(l14 (-3150 -850) (3750 1000)) + rect(l15 (-101 -851) (2 2)) + rect(l6 (-3201 1399) (450 900)) + rect(l6 (-450 -2250) (450 900)) + rect(l10 (1850 -900) (600 1200)) + ) + + # Outgoing pins and their connections to nets + pin(1 name(B)) + pin(2 name(A)) + pin(4 name(OUT)) + pin(5 name(VDD)) + pin(6 name(VSS)) + + # Devices and their connections + device(1 D$PMOS + location(1025 4950) + param(L 0.25) + param(W 1.5) + param(AS 0.675) + param(AD 0.375) + param(PS 3.9) + param(PD 2) + terminal(S 5) + terminal(G 1) + terminal(D 4) + terminal(B 5) + ) + device(2 D$PMOS$1 + location(1775 4950) + param(L 0.25) + param(W 1.5) + param(AS 0.375) + param(AD 0.675) + param(PS 2) + param(PD 3.9) + terminal(S 4) + terminal(G 2) + terminal(D 5) + terminal(B 5) + ) + device(3 D$NMOS + device(D$NMOS location(0 -1350)) + connect(0 S S) + connect(1 S S) + connect(0 G G) + connect(1 G G) + connect(0 D D) + connect(1 D D) + connect(0 B B) + connect(1 B B) + location(1025 2000) + param(L 0.25) + param(W 1.8) + param(AS 0.81) + param(AD 0.45) + param(PS 5.4) + param(PD 2.8) + terminal(S 6) + terminal(G 1) + terminal(D 3) + terminal(B 6) + ) + device(4 D$NMOS$1 + device(D$NMOS$1 location(0 -1350)) + connect(0 S S) + connect(1 S S) + connect(0 G G) + connect(1 G G) + connect(0 D D) + connect(1 D D) + connect(0 B B) + connect(1 B B) + location(1775 2000) + param(L 0.25) + param(W 1.8) + param(AS 0.45) + param(AD 0.81) + param(PS 2.8) + param(PD 5.4) + terminal(S 3) + terminal(G 2) + terminal(D 4) + terminal(B 6) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(NAND2_WITH_DIODES + + # Nets + net(1 name(A)) + net(2 name(B)) + net(3 name(OUT)) + net(4 name(VSS)) + net(5 name(VDD)) + net(6 name($1)) + + # Outgoing pins and their connections to nets + pin(1 name(A)) + pin(2 name(B)) + pin(3 name(OUT)) + pin(4 name(VSS)) + pin(5 name(VDD)) + + # Devices and their connections + device(1 PMOS + name('1') + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 5) + terminal(G 2) + terminal(D 3) + terminal(B 5) + ) + device(2 PMOS + name('2') + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 5) + terminal(G 1) + terminal(D 3) + terminal(B 5) + ) + device(3 NMOS + name('3') + param(L 0.25) + param(W 1.8) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 4) + terminal(G 2) + terminal(D 6) + terminal(B 4) + ) + device(4 NMOS + name('4') + param(L 0.25) + param(W 1.8) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 6) + terminal(G 1) + terminal(D 3) + terminal(B 4) + ) + + ) +) + +# Cross reference +xref( + circuit(NAND2_WITH_DIODES NAND2_WITH_DIODES match + xref( + net(3 6 match) + net(2 1 match) + net(1 2 match) + net(4 3 match) + net(5 5 match) + net(6 4 match) + pin(1 0 match) + pin(0 1 match) + pin(2 2 match) + pin(3 4 match) + pin(4 3 match) + device(3 3 match) + device(4 4 match) + device(1 1 match) + device(2 2 match) + ) + ) +) diff --git a/testdata/lvs/nand2_split_gate.oas b/testdata/lvs/nand2_split_gate.oas new file mode 100644 index 000000000..f0163a8d4 Binary files /dev/null and b/testdata/lvs/nand2_split_gate.oas differ diff --git a/testdata/lvs/nand2_split_gate_schematic.cir b/testdata/lvs/nand2_split_gate_schematic.cir new file mode 100644 index 000000000..6f8ec7d3c --- /dev/null +++ b/testdata/lvs/nand2_split_gate_schematic.cir @@ -0,0 +1,7 @@ + +.SUBCKT NAND2_WITH_DIODES A B OUT VSS VDD +M1 VDD B OUT VDD PMOS L=0.25U W=1.5U +M2 VDD A OUT VDD PMOS L=0.25U W=1.5U +M3 VSS B $1 VSS NMOS L=0.25U W=1.8U +M4 $1 A OUT VSS NMOS L=0.25U W=1.8U +.ENDS diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index d6645b73c..7f40be626 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -90,6 +90,13 @@ class DBNetlist_TestClass < TestBase nl.each_circuit { |i| names << i.name } assert_equal(names, [ c.name, cc.name ]) + assert_equal(nl.circuits_by_name("X*").collect { |c| c.name }, [ "XYZ" ]) + assert_equal(nl.circuits_by_name("???").collect { |c| c.name }, [ "XYZ", "UVW" ]) + assert_equal(nl.circuits_by_name("*").collect { |c| c.name }, [ "XYZ", "UVW" ]) + assert_equal(nl.circuits_by_name("P*").collect { |c| c.name }, []) + assert_equal(nl.circuits_by_name("x*").collect { |c| c.name }, []) + assert_equal(nl.circuits_by_name("").collect { |c| c.name }, []) + # same as _destroy nl.remove(c) @@ -1015,6 +1022,53 @@ END assert_equal(nl2.to_s, <<"END") circuit RINGO (IN=(null),OSC=(null),VSS=(null),VDD=(null)); end; +END + + end + + def test_13_JoinNets + + nl = RBA::Netlist::new + + dc = RBA::DeviceClassMOS3Transistor::new + dc.name = "NMOS" + nl.add(dc) + + dc = RBA::DeviceClassMOS3Transistor::new + dc.name = "PMOS" + nl.add(dc) + + nl.from_s(<<"END") +circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5); + subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN); + subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN); + subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2); + subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2); +end; +circuit PTRANS ($1=$1,$2=$2,$3=$3); + device PMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95); +end; +circuit NTRANS ($1=$1,$2=$2,$3=$3); + device NMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95); +end; +END + + c = nl.circuit_by_name("INV2") + c.join_nets(c.net_by_name("IN"), c.net_by_name("OUT")) + + assert_equal(nl.to_s, <<"END") +circuit INV2 (IN=IN,$2=$2,OUT=IN,$4=$4,$5=$5); + subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN); + subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN); + subcircuit PTRANS SC3 ($1=$5,$2=IN,$3=$2); + subcircuit NTRANS SC4 ($1=$4,$2=IN,$3=$2); +end; +circuit PTRANS ($1=$1,$2=$2,$3=$3); + device PMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); +end; +circuit NTRANS ($1=$1,$2=$2,$3=$3); + device NMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); +end; END end diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index be77f5bb4..d256aa849 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -986,6 +986,57 @@ END end + def test_11 + + nls = <