Merge pull request #514 from KLayout/issue-482

Implemented #482 (split gate option aka join_symmetric_nets)
This commit is contained in:
Matthias Köfferlein 2020-02-27 18:46:02 +01:00 committed by GitHub
commit 56868eda85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
138 changed files with 1773 additions and 263 deletions

View File

@ -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<db::Device *> (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<db::SubCircuit *> (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);

View File

@ -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
*/

View File

@ -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<db::PolygonRef> &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;
}

View File

@ -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);
};
}

View File

@ -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;
};
/**

View File

@ -27,20 +27,50 @@
#include "tlTimer.h"
#include "tlEquivalenceClusters.h"
#include "tlLog.h"
#include "tlEnv.h"
#include "tlInternational.h"
#include <cstring>
// 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<size_t>::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<NetGraphNode>::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<NetGraphNode>::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) {
tl::info << i->to_string () << tl::noendl;
if (options ()->debug_netgraph) {
for (std::vector<NetGraphNode>::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<const db::SubCircuit *, NetGraphNode>::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<const db::SubCircuit *, NetGraphNode>::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<const db::SubCircuit *, NetGraphNode>::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<const NetGraphNode *> 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<NetGraphNode::Transition>::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<NetGraphNode::Transition>::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<NetGraphNode::Transition>::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<NetGraphNode::Transition>::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<const NetGraphNode *> &nodes, std::vector<const NetGraphNode *> &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::vector<const NetGraphNode *
TentativeNodeMapping::map_pair (tentative, this, ni, data->other, 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<const NetGraphNode *
// their names differ -> 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::vector<const NetGraphNode *
TentativeNodeMapping::map_pair (tentative, this, ni, data->other, 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::vector<const NetGraphNode *
} else if (nr->num * 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::vector<const NetGraphNode *
TentativeNodeMapping::map_pair_from_unknown (&tn, this, ni, data->other, 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::vector<const NetGraphNode *
}
if (! any && tentative) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "mismatch.";
#endif
if (options ()->debug_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::vector<const NetGraphNode *
TentativeNodeMapping::map_pair (tentative, this, ni, data->other, 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::vector<const NetGraphNode *
}
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "finished analysis of ambiguity group with " << nr->num << " 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<const NetGraphNode *> &identical_nodes, std::set<size_t> &considered_nodes, const std::set<size_t> &symmetry_group, std::list<std::set<size_t> > &symmetry_groups)
{
std::set<size_t> cids;
std::set<size_t> new_symmetry_group;
NetGraphNode::edge_type::first_type edge;
std::vector<NetGraphNode::edge_type> common_nodes_first;
bool has_candidate = true;
for (std::set<size_t>::const_iterator g = symmetry_group.begin (); g != symmetry_group.end () && has_candidate; ++g) {
std::vector<NetGraphNode::edge_type> 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<size_t> 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<const db::Circuit *, CircuitMapper> 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<const NetGraphNode *> 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<const NetGraphNode *> identical_nodes;
for (std::vector<const NetGraphNode *>::const_iterator np = nodes.begin (); np + 1 != nodes.end (); ++np) {
if (*np[0] == *np[1]) {
identical_nodes.same (np[0], np[1]);
}
}
std::list<std::set<size_t> > symmetry_groups;
std::set<size_t> visited;
for (std::vector<const NetGraphNode *>::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<size_t> considered_nodes;
considered_nodes.insert (node_id);
std::set<size_t> 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<std::set<size_t> >::const_iterator g = symmetry_groups.begin (); g != symmetry_groups.end (); ++g) {
tl::info << " [" << index << "] " << tl::noendl;
for (std::set<size_t>::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<std::set<size_t> >::const_iterator g = symmetry_groups.begin (); g != symmetry_groups.end (); ++g) {
for (std::set<size_t>::const_iterator i = g->begin (); i != g->end (); ++i) {
if (i != g->begin ()) {
circuit->join_nets (const_cast<db::Net *> (graph.net_by_node_index (*g->begin ())), const_cast<db::Net *> (graph.net_by_node_index (*i)));
}
}
}
}
}

View File

@ -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 &);

View File

@ -1201,6 +1201,12 @@ Class<db::Circuit> 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<db::Circuit *>
circuits_by_name (db::Netlist *netlist, const std::string &name_pattern)
{
std::vector<db::Circuit *> 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<db::Netlist> 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<db::Netlist> 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. "

View File

@ -557,6 +557,20 @@ Class<db::NetlistComparer> 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"

View File

@ -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<unsigned int, const db::Region *> 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)

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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"
);
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/dvb/scripts/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/master/scripts/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/dvb/scripts/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/master/scripts/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>
@ -151,7 +151,7 @@ l1and2, l1minus2 = cheat("UNIT_CELL) do
end
</pre>
</p><p>
(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).
</p><p>
@ -234,7 +234,6 @@ See <a href="/about/drc_ref_netter.xml#connect_global">Netter#connect_global</a>
<a name="connect_implicit"/><p>Usage:</p>
<ul>
<li><tt>connect_implicit(label_pattern)</tt></li>
<li><tt>connect_implicit(cell_pattern, label_pattern)</tt></li>
</ul>
<p>
See <a href="/about/drc_ref_netter.xml#connect_implicit">Netter#connect_implicit</a> for a description of that function.
@ -769,7 +768,7 @@ a new target will be set up.
</p><p>
<ul>
<li>A string "@n" (n is an integer) specifying output to a layout in the current panel </li>
<li>A string "@+" specifying output to a new layout in the current panel</li>
<li>A string "@+" specifying output to a new layout in the current panel </li>
<li>A layout filename </li>
<li>A <class_doc href="Layout">Layout</class_doc> object </li>
<li>A <class_doc href="Cell">Cell</class_doc> object </li>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/dvb/scripts/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/master/scripts/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>
@ -751,6 +751,19 @@ The following image shows the effect of the "interacting" method (input1: red, i
</tr>
</table>
</p>
<h2>"intersections" - Returns the intersection points of intersecting edge segments for two edge collections</h2>
<keyword name="intersections"/>
<a name="intersections"/><p>Usage:</p>
<ul>
<li><tt>layer.intersections(edges)</tt></li>
</ul>
<p>
This operation is similar to the "&amp;" operator, but it does also report intersection points
between non-colinear, but intersection edges. Such points are reported as point-like,
degenerated edge objects.
</p><p>
This method is available for edge layers. The argument must be an edge layer.
</p>
<h2>"is_box?" - Returns true, if the region contains a single box</h2>
<keyword name="is_box?"/>
<a name="is_box?"/><p>Usage:</p>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/dvb/scripts/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/master/scripts/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>
@ -187,17 +187,17 @@ to shapes belonging to tie-down diodes.
<li><tt>connect_implicit(cell_pattern, label_pattern)</tt></li>
</ul>
<p>
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.
</p><p>
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.

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/dvb/scripts/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/master/scripts/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/dvb/scripts/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/master/scripts/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/dvb/scripts/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/master/scripts/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>
@ -46,6 +46,15 @@ See <a href="/about/lvs_ref_netter.xml#compare">Netter#compare</a> for a descrip
<p>
See <a href="/about/lvs_ref_netter.xml#equivalent_pins">Netter#equivalent_pins</a> for a description of that function.
</p>
<h2>"join_symmetric_nets" - Joins symmetric nets of selected circuits on the extracted netlist</h2>
<keyword name="join_symmetric_nets"/>
<a name="join_symmetric_nets"/><p>Usage:</p>
<ul>
<li><tt>join_symmetric_nets(circuit_filter)</tt></li>
</ul>
<p>
See <a href="/about/lvs_ref_netter.xml#join_symmetric_nets">Netter#join_symmetric_nets</a> for a description of that function.
</p>
<h2>"max_branch_complexity" - Configures the maximum branch complexity for ambiguous net matching</h2>
<keyword name="max_branch_complexity"/>
<a name="max_branch_complexity"/><p>Usage:</p>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/dvb/scripts/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/master/scripts/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>
@ -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
<a href="#schematic">schematic</a>.
</p>
<h2>"join_symmetric_nets" - Joins symmetric nets of selected circuits on the extracted netlist</h2>
<keyword name="join_symmetric_nets"/>
<a name="join_symmetric_nets"/><p>Usage:</p>
<ul>
<li><tt>join_symmetric_nets(circuit_filter)</tt></li>
</ul>
<p>
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).
</p><p>
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.
</p><p>
For the symmetry detection, the specified constraints (e.g. tolerances,
device filters etc.) apply.
</p>
<h2>"lvs_data" - Gets the internal <class_doc href="LayoutVsSchematic">LayoutVsSchematic</class_doc> object</h2>
<keyword name="lvs_data"/>
<a name="lvs_data"/><p>Usage:</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Some files were not shown because too many files have changed in this diff Show More