From 58de38739abbc637cbbecf477f7bc8606fa7261a Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Tue, 25 Feb 2020 00:07:48 +0100
Subject: [PATCH 01/10] WIP: some refactoring, debugging output for netlist
compare
Abstraction: a central getenv() feature to wrap all the system-specific things
Netlist compare debug and options can be enabled through environment variables:
KLAYOUT_NETLIST_COMPARE_DEBUG_NETCOMPARE=1: print netlist compare debug info
KLAYOUT_NETLIST_COMPARE_DEBUG_NETGRAPH=1: print net grapg
KLAYOUT_NETLIST_COMPARE_CASE_SENSITIVE=1: make netlist compare case sensitive
---
src/db/db/dbNetlistCompare.cc | 319 ++++++++++--------
src/lay/lay/layApplication.cc | 8 -
src/lay/lay/layApplication.h | 10 -
src/lay/lay/laySystemPaths.cc | 55 +--
.../tools/xor/lay_plugin/layXORToolDialog.cc | 3 +-
src/tl/tl/tl.pro | 6 +-
src/tl/tl/tlEnv.cc | 85 +++++
src/tl/tl/tlEnv.h | 65 ++++
src/tl/tl/tlExpression.cc | 8 +-
src/tl/tl/tlLog.cc | 22 +-
src/tl/tl/tlUnitTest.cc | 9 +-
src/tl/unit_tests/tlExpression.cc | 4 +-
12 files changed, 358 insertions(+), 236 deletions(-)
create mode 100644 src/tl/tl/tlEnv.cc
create mode 100644 src/tl/tl/tlEnv.h
diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc
index 237fa1059..4ac53a39a 100644
--- a/src/db/db/dbNetlistCompare.cc
+++ b/src/db/db/dbNetlistCompare.cc
@@ -27,20 +27,49 @@
#include "tlTimer.h"
#include "tlEquivalenceClusters.h"
#include "tlLog.h"
+#include "tlEnv.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 +92,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 +1541,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 +1570,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 +1588,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 +1646,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 +1656,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 +1673,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 +1684,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 +1713,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 +1764,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 +1775,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 +1800,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 +1812,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 +1897,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 +1924,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 +2073,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 +2087,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 +2129,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 +2195,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 +2228,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 +2250,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 +2292,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 +2559,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 +2878,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 +2886,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 +2918,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 +2975,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) {
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/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/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..9b3e03e89
--- /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 (QString ((const QChar *) 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
-(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 9ea749e22f91abfa96d5f993b1f13a2478e9c61b..5964e19393309b82661763f2bdd4de28817ccac8 100644
GIT binary patch
literal 5120
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
literal 5107
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
VMsNRH6xfUdj1A0B7wBCM`3Gw0pf>;j
diff --git a/src/lay/lay/doc/images/drc_and2.png b/src/lay/lay/doc/images/drc_and2.png
index 87bd69131a52278e05fe264a0b20c6b931f35889..f6a9cfe6bf0916acfe16a2f7512f32105f442bb2 100644
GIT binary patch
literal 6074
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
Xe$U8-A{;mYB>h}Yxu2|b3QGG2ZcU~x
delta 3679
zcmcgvX;4$ywoX7)AR#D-VT`l_+6bgukih^lD9vL~Tful4LKKW50zt-L2$P`^goYzv
z%hWgk8X(cgAVU};f`BH-5MqGPAT$t_2nk36a--e%-l|*G^{VStz4Pnrbj${2X+fa-Kp;^OB-C%MG6R7oypa1ra5Bc*=$r)28y@Uu7TI(g1
zHKklBV1J(EO5JNanXq&)vBsQ&^`mm?$2ah!4huywUGc5_m6MVWdq}D=iY+w7_2E7o
zrCRkglV{mhVv*g;d*J@T3lrKgeOmjYP&}iiB2NtW>a_@w)4h&MArQdQ7(LJKGos#C
zw41=yPp{UqLGtvtHBx@|>|$pbdiCduq!Rx=L$;+X~VX)^q(g)|mW+gUg(3QYvP%O{REQf7{{oMi^z_
zrI4sU^S~vt6R9K(aGZX!$c~yLP+GR+7sFP_=B4!E%
z|KlYHf0u^QVpuKr#sF92%qcczOcgzv`0%}Wl5VO-Kd(nxp-zj05Rz-<(S
zzaLwf7-DE5@ehbRQgJofwRrPnc)~7yXjUq9N_hA;VXn7bQ=RATMbwF(w|t()f#91z
z#{>HCo%+J#%pPImGp1O0o70@I9%b_*OfMkZYP(z;G5`%iLPR;^zCfq^g`6=siMep`
zc4$3s(TU74(5p$~T*mN!U(L-F@b%+wk*3!Rlhd`lFQ`BTeVuZ%icp)KaPJTu!u50&
zhc?>TlibME!2!+uA*j3W2f70|I5tiMYMj2I&Uo0_~zAi~YpV{))Rv;-SM(w`T*?)5&w(k~UJ6S?V3%
z03&OT7b~AF8%%a?IR&@%ailZaUz}k+G`5YDM4JC!QT`f)^#6kL*C3?7ow2`O%cG~B
z1CdQ*`H1hEWfc}LXqidwyyGLB_}P}rf)GpLbcAayWA2Yyb!fwPsmQh=$$3j6xH81X
zaUQObdiDsp6#2xXSm6I7{Q?cMSBs)z3iR8X&6JalQ=bwId
z%~WOpF(=q30nFB5(gY1A_*|e@%2H7d2x+L4id~fxW&24BhGeA#aP=DA`qrFka*nEyLUN!kb^J)UASke2_?!FN0RfZ^8-WCllw8bN)Rv#
zfGQ!uEWl354{Qq*Dfxrl0jbgf5`T2%)740y^*Kp74;!=_jTm0fnJLW