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.
- A string "@n" (n is an integer) specifying output to a layout in the current panel
-- A string "@+" specifying output to a new layout in the current panel
+- A string "@+" specifying output to a new layout in the current panel
- A layout filename
- A Layout object
- A Cell object
diff --git a/src/lay/lay/doc/about/drc_ref_layer.xml b/src/lay/lay/doc/about/drc_ref_layer.xml
index fb9d591c5..faa14fb73 100644
--- a/src/lay/lay/doc/about/drc_ref_layer.xml
+++ b/src/lay/lay/doc/about/drc_ref_layer.xml
@@ -1,7 +1,7 @@
-
+
@@ -751,6 +751,19 @@ The following image shows the effect of the "interacting" method (input1: red, i
+"intersections" - Returns the intersection points of intersecting edge segments for two edge collections
+
+Usage:
+
+- layer.intersections(edges)
+
+
+This operation is similar to the "&" operator, but it does also report intersection points
+between non-colinear, but intersection edges. Such points are reported as point-like,
+degenerated edge objects.
+
+This method is available for edge layers. The argument must be an edge layer.
+
"is_box?" - Returns true, if the region contains a single box
Usage:
diff --git a/src/lay/lay/doc/about/drc_ref_netter.xml b/src/lay/lay/doc/about/drc_ref_netter.xml
index 406d7eb91..8324c8c14 100644
--- a/src/lay/lay/doc/about/drc_ref_netter.xml
+++ b/src/lay/lay/doc/about/drc_ref_netter.xml
@@ -1,7 +1,7 @@
-
+
@@ -187,17 +187,17 @@ to shapes belonging to tie-down diodes.
- connect_implicit(cell_pattern, label_pattern)
-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