diff --git a/Jenkinsfile b/Jenkinsfile index 93d1a813e..e3033ad39 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -16,6 +16,7 @@ node("master") { stage("Checkout sources") { checkout scm + checkout_private() } diff --git a/src/buddies/src/bd/strmrun.cc b/src/buddies/src/bd/strmrun.cc index 796966259..f76976389 100644 --- a/src/buddies/src/bd/strmrun.cc +++ b/src/buddies/src/bd/strmrun.cc @@ -87,5 +87,6 @@ BD_PUBLIC int strmrun (int argc, char *argv[]) lym::Macro macro; macro.load_from (script); + macro.set_file_path (script); return macro.run (); } diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index 8b55c6c35..1a2de4246 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -70,7 +70,11 @@ struct basic_array_iterator virtual long index_a () const { return -1; } virtual long index_b () const { return -1; } - + + virtual size_t quad_id () const { return 0; } + virtual box_type quad_box () const { return box_type::world (); } + virtual void skip_quad () { } + virtual disp_type get () const = 0; virtual basic_array_iterator *clone () const = 0; @@ -111,8 +115,12 @@ struct ArrayBase virtual bool equal (const ArrayBase *) const = 0; + virtual bool fuzzy_equal (const ArrayBase *) const = 0; + virtual bool less (const ArrayBase *) const = 0; + virtual bool fuzzy_less (const ArrayBase *) const = 0; + virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self = false, void *parent = 0) const = 0; bool in_repository; @@ -554,6 +562,12 @@ struct regular_array return (m_a == d->m_a && m_b == d->m_b && m_amax == d->m_amax && m_bmax == d->m_bmax); } + virtual bool fuzzy_equal (const ArrayBase *b) const + { + const regular_array *d = static_cast *> (b); + return (m_a.equal (d->m_a) && m_b.equal (d->m_b) && m_amax == d->m_amax && m_bmax == d->m_bmax); + } + virtual bool less (const ArrayBase *b) const { const regular_array *d = static_cast *> (b); @@ -562,6 +576,14 @@ struct regular_array m_amax < d->m_amax || (m_amax == d->m_amax && m_bmax < d->m_bmax))))); } + virtual bool fuzzy_less (const ArrayBase *b) const + { + const regular_array *d = static_cast *> (b); + return m_a.less (d->m_a) || (m_a.equal (d->m_a) && ( + m_b.less (d->m_b) || (m_b.equal (d->m_b) && ( + m_amax < d->m_amax || (m_amax == d->m_amax && m_bmax < d->m_bmax))))); + } + virtual bool is_regular_array (vector_type &a, vector_type &b, unsigned long &amax, unsigned long &bmax) const { a = m_a; @@ -702,6 +724,18 @@ struct regular_complex_array return regular_array::equal (b); } + virtual bool fuzzy_equal (const ArrayBase *b) const + { + const regular_complex_array *d = static_cast *> (b); + if (fabs (m_acos - d->m_acos) > epsilon) { + return false; + } + if (fabs (m_mag - d->m_mag) > epsilon) { + return false; + } + return regular_array::fuzzy_equal (b); + } + virtual bool less (const ArrayBase *b) const { const regular_complex_array *d = static_cast *> (b); @@ -714,6 +748,18 @@ struct regular_complex_array return regular_array::less (b); } + virtual bool fuzzy_less (const ArrayBase *b) const + { + const regular_complex_array *d = static_cast *> (b); + if (fabs (m_acos - d->m_acos) > epsilon) { + return m_acos < d->m_acos; + } + if (fabs (m_mag - d->m_mag) > epsilon) { + return m_mag < d->m_mag; + } + return regular_array::fuzzy_less (b); + } + virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const { if (!no_self) { @@ -806,6 +852,21 @@ struct iterated_array_iterator return new iterated_array_iterator (*this); } + virtual size_t quad_id () const + { + return m_t.quad_id (); + } + + virtual box_type quad_box () const + { + return m_t.quad_box (); + } + + virtual void skip_quad () + { + m_t.skip_quad (); + } + private: box_tree_const_iterator m_b, m_e; box_tree_touching_iterator m_t; @@ -964,6 +1025,20 @@ struct iterated_array return true; } + virtual bool fuzzy_equal (const ArrayBase *b) const + { + const iterated_array *d = static_cast *> (b); + if (m_v.size () != d->m_v.size ()) { + return false; + } + for (const_iterator p1 = m_v.begin (), p2 = d->m_v.begin (); p1 != m_v.end (); ++p1, ++p2) { + if (! p1->equal (*p2)) { + return false; + } + } + return true; + } + virtual bool less (const ArrayBase *b) const { const iterated_array *d = static_cast *> (b); @@ -978,6 +1053,20 @@ struct iterated_array return false; } + virtual bool fuzzy_less (const ArrayBase *b) const + { + const iterated_array *d = static_cast *> (b); + if (m_v.size () != d->m_v.size ()) { + return (m_v.size () < d->m_v.size ()); + } + for (const_iterator p1 = m_v.begin (), p2 = d->m_v.begin (); p1 != m_v.end (); ++p1, ++p2) { + if (! p1->equal (*p2)) { + return (p1->less (*p2)); + } + } + return false; + } + virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const { if (!no_self) { @@ -1085,6 +1174,18 @@ struct iterated_complex_array return iterated_array::equal (b); } + virtual bool fuzzy_equal (const ArrayBase *b) const + { + const iterated_complex_array *d = static_cast *> (b); + if (fabs (m_acos - d->m_acos) > epsilon) { + return false; + } + if (fabs (m_mag - d->m_mag) > epsilon) { + return false; + } + return iterated_array::fuzzy_equal (b); + } + virtual bool less (const ArrayBase *b) const { const iterated_complex_array *d = static_cast *> (b); @@ -1097,6 +1198,18 @@ struct iterated_complex_array return iterated_array::less (b); } + virtual bool fuzzy_less (const ArrayBase *b) const + { + const iterated_complex_array *d = static_cast *> (b); + if (fabs (m_acos - d->m_acos) > epsilon) { + return m_acos < d->m_acos; + } + if (fabs (m_mag - d->m_mag) > epsilon) { + return m_mag < d->m_mag; + } + return iterated_array::fuzzy_less (b); + } + virtual complex_trans_type complex_trans (const simple_trans_type &s) const { return complex_trans_type (s, m_acos, m_mag); @@ -1187,7 +1300,12 @@ struct single_complex_inst return true; } - virtual bool less (const ArrayBase *b) const + virtual bool fuzzy_equal (const ArrayBase *b) const + { + return equal (b); + } + + virtual bool less (const ArrayBase *b) const { const double epsilon = 1e-10; const single_complex_inst *d = static_cast *> (b); @@ -1200,6 +1318,11 @@ struct single_complex_inst return false; } + virtual bool fuzzy_less (const ArrayBase *b) const + { + return less (b); + } + virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const { if (!no_self) { @@ -1420,6 +1543,36 @@ struct array_iterator return mp_base ? mp_base->index_b () : -1; } + /** + * @brief For iterators supporting quads (iterated arrays), this method will return the quad ID + */ + size_t quad_id () const + { + return mp_base ? mp_base->quad_id () : 0; + } + + /** + * @brief For iterators supporting quads (iterated arrays), this method will return the quad bounding box + * + * Note that this method will only return a valid quad box is the quad_id is non-null. + * + * This method will return the bounding box of all array offsets in the quad. + */ + db::box quad_box () const + { + return mp_base ? mp_base->quad_box () : db::box::world (); + } + + /** + * @brief For iterators supporting quads (iterated arrays), this method will skip the current quad + */ + void skip_quad () + { + if (mp_base) { + mp_base->skip_quad (); + } + } + private: trans_type m_trans; basic_array_iterator *mp_base; @@ -1592,6 +1745,32 @@ struct array // .. nothing yet .. } + /** + * @brief The iterated array constructor + * + * This is basically a convenience function that creates + * an appropriate basic_array object. + */ + template + array (const Obj &obj, const trans_type &trans, Iter from, Iter to) + : m_obj (obj), m_trans (trans), mp_base (new iterated_array (from, to)) + { + // .. nothing yet .. + } + + /** + * @brief The complex iterated array constructor + * + * This is basically a convenience function that creates + * an appropriate basic_array object. + */ + template + array (const Obj &obj, const complex_trans_type &ct, Iter from, Iter to) + : m_obj (obj), m_trans (ct), mp_base (new iterated_complex_array (ct.rcos (), ct.mag (), from, to)) + { + // .. nothing yet .. + } + /** * @brief The singular complex instance constructor * @@ -1835,6 +2014,32 @@ struct array } } + /** + * @brief Gets the bounding box from the iterator's current quad + * + * The bounding box is that of all objects in the current quad and + * is confined to the array's total bounding box. + */ + template + box_type quad_box (const Iter &iter, const BoxConv &bc) const + { + box_type bb; + if (mp_base) { + bb = mp_base->bbox (box_type (0, 0, 0, 0)); + } + bb &= iter.quad_box (); + + if (mp_base) { + if (mp_base->is_complex ()) { + return bb * box_type (mp_base->complex_trans (simple_trans_type (m_trans)) * bc (m_obj)); + } else { + return bb * (m_trans * bc (m_obj)); + } + } else { + return bb * (m_trans * bc (m_obj)); + } + } + /** * @brief The number of single instances in the array */ @@ -1918,6 +2123,21 @@ struct array } } + /** + * @brief Compare operator for equality (fuzzy version) + */ + bool equal (const array &d) const + { + if (! mp_base) { + return (m_trans.equal (d.m_trans) && m_obj == d.m_obj && ! d.mp_base); + } else { + if (! m_trans.equal (d.m_trans) || ! (m_obj == d.m_obj) || type () != d.type ()) { + return false; + } + return mp_base && mp_base->fuzzy_equal (d.mp_base); + } + } + /** * @brief A sorting order criterion */ @@ -1943,6 +2163,31 @@ struct array } } + /** + * @brief A fuzzy sorting order criterion + */ + bool less (const array &d) const + { + if (! (m_obj == d.m_obj)) { + return (m_obj < d.m_obj); + } + if (! m_trans.equal (d.m_trans)) { + return m_trans.less (d.m_trans); + } + if (type () != d.type ()) { + return (type () < d.type ()); + } + if (mp_base == d.mp_base) { + return false; + } else if (! mp_base) { + return true; + } else if (! d.mp_base) { + return false; + } else { + return mp_base->fuzzy_less (d.mp_base); + } + } + /** * @brief Compare operator * @@ -2263,7 +2508,7 @@ struct array } db::mem_stat (stat, purpose, cat, m_obj, true, (void *) this); if (mp_base) { - db::mem_stat (stat, purpose, cat, *mp_base, false, (void *) this); + mp_base->mem_stat (stat, purpose, cat, false, (void *) this); } } diff --git a/src/db/db/dbBoxTree.h b/src/db/db/dbBoxTree.h index 0e3a8ae67..982993496 100644 --- a/src/db/db/dbBoxTree.h +++ b/src/db/db/dbBoxTree.h @@ -168,40 +168,43 @@ public: typedef typename Tree::coord_type coord_type; typedef typename Tree::box_type box_type; - box_tree_node (box_tree_node *parent, const point_type ¢er, unsigned int quad) - : m_center (center) + box_tree_node (box_tree_node *parent, const point_type ¢er, const box_type &qbox, unsigned int quad) { - for (int i = 0; i < 5; ++i) { - m_lenq[i] = 0; - } - for (int i = 0; i < 4; ++i) { - mp_children[i] = 0; - } - mp_parent = (box_tree_node *)((char *) parent + quad); - if (parent) { - parent->mp_children [quad] = this; + point_type corner; + if (quad == 0) { + corner = qbox.upper_right (); + } else if (quad == 1) { + corner = qbox.upper_left (); + } else if (quad == 2) { + corner = qbox.lower_left (); + } else if (quad == 3) { + corner = qbox.lower_right (); } + + init (parent, center, corner, quad); } ~box_tree_node () { for (int i = 0; i < 4; ++i) { - if (mp_children [i]) { - delete mp_children [i]; - mp_children [i] = 0; + box_tree_node *c = child (i); + if (c) { + delete c; } } } box_tree_node *clone (box_tree_node *parent = 0, unsigned int quad = 0) const { - box_tree_node *n = new box_tree_node (parent, m_center, quad); - for (unsigned int i = 0; i < 5; ++i) { - n->m_lenq[i] = m_lenq[i]; - } + box_tree_node *n = new box_tree_node (parent, m_center, m_corner, quad); + n->m_lenq = m_lenq; + n->m_len = m_len; for (unsigned int i = 0; i < 4; ++i) { - if (mp_children[i]) { - mp_children[i]->clone (n, i); + box_tree_node *c = child (i); + if (c) { + c->clone (n, i); + } else { + n->m_childrefs [i] = m_childrefs [i]; } } return n; @@ -209,17 +212,39 @@ public: box_tree_node *child (int i) const { - return mp_children [i]; + if ((m_childrefs [i] & 1) == 0) { + return reinterpret_cast (m_childrefs [i]); + } else { + return 0; + } } void lenq (int i, size_t l) { - m_lenq[i + 1] = l; + if (i < 0) { + m_lenq = l; + } else { + box_tree_node *c = child (i); + if (c) { + c->m_len = l; + } else { + m_childrefs [i] = l * 2 + 1; + } + } } size_t lenq (int i) const { - return m_lenq[i + 1]; + if (i < 0) { + return m_lenq; + } else { + box_tree_node *c = child (i); + if (c) { + return c->m_len; + } else { + return m_childrefs [i] >> 1; + } + } } box_tree_node *parent () const @@ -238,8 +263,8 @@ public: stat->add (typeid (*this), (void *) this, sizeof (*this), sizeof (*this), parent, purpose, cat); } for (int i = 0; i < 4; ++i) { - if (mp_children [i]) { - mp_children [i]->mem_stat (stat, purpose, cat, no_self, parent); + if (child (i)) { + child (i)->mem_stat (stat, purpose, cat, no_self, parent); } } } @@ -249,14 +274,52 @@ public: return m_center; } + box_type quad_box (int quad) const + { + box_type qb = box_type::world (); + if (parent ()) { + qb = box_type (m_corner, parent ()->center ()); + } + + switch (quad) { + case 0: return box_type (m_center, qb.upper_right ()); + case 1: return box_type (m_center, qb.upper_left ()); + case 2: return box_type (m_center, qb.lower_left ()); + case 3: return box_type (m_center, qb.lower_right ()); + default: return qb; + } + } + private: box_tree_node *mp_parent; - size_t m_lenq [5]; - box_tree_node *mp_children [4]; - point_type m_center; + size_t m_lenq, m_len; + size_t m_childrefs [4]; + point_type m_center, m_corner; box_tree_node (const box_tree_node &d); box_tree_node &operator= (const box_tree_node &d); + + box_tree_node (box_tree_node *parent, const point_type ¢er, const point_type &corner, unsigned int quad) + { + init (parent, center, corner, quad); + } + + void init (box_tree_node *parent, const point_type ¢er, const point_type &corner, unsigned int quad) + { + m_center = center; + m_corner = corner; + + m_lenq = m_len = 0; + for (int i = 0; i < 4; ++i) { + m_childrefs [i] = 0; + } + + mp_parent = (box_tree_node *)((char *) parent + quad); + if (parent) { + m_len = (parent->m_childrefs [quad] >> 1); + parent->m_childrefs [quad] = size_t (this); + } + } }; /** @@ -459,28 +522,9 @@ public: box_type quad_box () const { if (! mp_node) { - return box_type::world (); - } else { - - point_type c = mp_node->center (); - box_type qb; - if (! mp_node->parent ()) { - qb = box_type::world (); - } else { - point_type pc = mp_node->parent ()->center (); - qb = box_type (c - (pc - c), pc); - } - - switch (m_quad) { - case 0: return box_type (c, qb.upper_right ()); - case 1: return box_type (c, qb.upper_left ()); - case 2: return box_type (c, qb.lower_left ()); - case 3: return box_type (c, qb.lower_right ()); - default: return qb; - } - + return mp_node->quad_box (m_quad); } } @@ -577,12 +621,16 @@ private: return m_quad < 4; } - // down one level + // down as many levels as required for the next non-empty quad // returns true if this is possible bool down () { - box_tree_node *c = mp_node->child (m_quad); - if (c) { + while (true) { + + box_tree_node *c = mp_node->child (m_quad); + if (! c) { + return false; + } mp_node = c; m_quad = -1; @@ -595,12 +643,11 @@ private: // nothing to visit: up again up (); return false; - } else { + } else if (m_quad < 0) { + // stay in main chunk return true; } - } else { - return false; } } @@ -670,7 +717,7 @@ private: * whose box overlaps or touches a specified test box. */ -template +template class box_tree { public: @@ -1175,7 +1222,16 @@ private: // the bins are: overall, ur, ul, ll, lr, empty element_iterator qloc [6] = { from, from, from, from, from, from }; - point_type center (bbox.center ()); + point_type center; + if (bbox.width () < bbox.height () / thin_aspect) { + // separate by height only + center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2); + } else if (bbox.height () < bbox.width () / thin_aspect) { + // separate by width only + center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ()); + } else { + center = bbox.center (); + } for (element_iterator e = from; e != to; ++e) { @@ -1224,7 +1280,7 @@ private: if (nn >= min_quads) { // create a new node representing this tree - box_tree_node *node = new box_tree_node (parent, center, quad); + box_tree_node *node = new box_tree_node (parent, center, bbox, quad); if (parent == 0) { mp_root = node; } @@ -1460,28 +1516,9 @@ public: box_type quad_box () const { if (! mp_node) { - return box_type::world (); - } else { - - point_type c = mp_node->center (); - box_type qb; - if (! mp_node->parent ()) { - qb = box_type::world (); - } else { - point_type pc = mp_node->parent ()->center (); - qb = box_type (c - (pc - c), pc); - } - - switch (m_quad) { - case 0: return box_type (c, qb.upper_right ()); - case 1: return box_type (c, qb.upper_left ()); - case 2: return box_type (c, qb.lower_left ()); - case 3: return box_type (c, qb.lower_right ()); - default: return qb; - } - + return mp_node->quad_box (m_quad); } } @@ -1578,12 +1615,16 @@ private: return m_quad < 4; } - // down one level + // down as many levels as required for the next non-empty quad // returns true if this is possible bool down () { - box_tree_node *c = mp_node->child (m_quad); - if (c) { + while (true) { + + box_tree_node *c = mp_node->child (m_quad); + if (! c) { + return false; + } mp_node = c; m_quad = -1; @@ -1596,12 +1637,11 @@ private: // nothing to visit: up again up (); return false; - } else { + } else if (m_quad < 0) { + // stay in main chunk return true; } - } else { - return false; } } @@ -1638,7 +1678,7 @@ private: * is sorted. */ -template +template class unstable_box_tree { public: @@ -2103,7 +2143,16 @@ private: } obj_iterator qloc [5] = { from, from, from, from, from }; - point_type center (bbox.center ()); + point_type center; + if (bbox.width () < bbox.height () / thin_aspect) { + // separate by height only + center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2); + } else if (bbox.height () < bbox.width () / thin_aspect) { + // separate by width only + center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ()); + } else { + center = bbox.center (); + } for (obj_iterator e = from; e != to; ++e) { @@ -2158,7 +2207,7 @@ private: if (nn >= min_quads) { // create a new node representing this tree - box_tree_node *node = new box_tree_node (parent, center, quad); + box_tree_node *node = new box_tree_node (parent, center, bbox, quad); if (parent == 0) { mp_root = node; } diff --git a/src/db/db/dbCell.h b/src/db/db/dbCell.h index e4b2f3a4a..56e5e6b9a 100644 --- a/src/db/db/dbCell.h +++ b/src/db/db/dbCell.h @@ -301,6 +301,30 @@ public: return m_instances.transform_into (ref, t); } + /** + * @brief Transforms the cell by the given transformation. + * + * The transformation is applied to all instances and shapes. Magnified transformations will + * render magnified instances. See \transform_into for a version which avoids this. + * + * @param t The transformation to apply + */ + template + void transform (const Trans &t) + { + m_instances.transform (t); + for (typename shapes_map::iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) { + if (! s->second.empty ()) { + // Note: don't use the copy ctor here - it will copy the attachment to the manager + // and create problems when destroyed. Plus: swap would be more efficient. But by using + // assign_transformed we get undo support for free. + shapes_type d; + d = s->second; + s->second.assign_transformed (d, t); + } + } + } + /** * @brief Transforms the cell into a new coordinate system. * diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index a2d5036bb..0c80b380a 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -333,20 +333,40 @@ void Circuit::remove_pin (size_t id) void Circuit::add_net (Net *net) { + if (! net) { + return; + } + if (net->circuit ()) { + throw tl::Exception (tl::to_string (tr ("Net already part of a circuit"))); + } + m_nets.push_back (net); net->set_circuit (this); } void Circuit::remove_net (Net *net) { + if (! net) { + return; + } + if (net->circuit () != this) { + throw tl::Exception (tl::to_string (tr ("Net not withing given circuit"))); + } + m_nets.erase (net); } void Circuit::join_nets (Net *net, Net *with) { + if (! net) { + return; + } if (net == with || ! with) { return; } + if (net->circuit () != this || with->circuit () != this) { + throw tl::Exception (tl::to_string (tr ("Nets not withing given circuit"))); + } while (with->begin_terminals () != with->end_terminals ()) { db::Device *device = const_cast (with->begin_terminals ()->device ()); @@ -372,6 +392,13 @@ void Circuit::join_nets (Net *net, Net *with) void Circuit::add_device (Device *device) { + if (! device) { + return; + } + if (device->circuit ()) { + throw tl::Exception (tl::to_string (tr ("Device already in a circuit"))); + } + device->set_circuit (this); size_t id = 0; @@ -386,11 +413,25 @@ void Circuit::add_device (Device *device) void Circuit::remove_device (Device *device) { + if (! device) { + return; + } + if (device->circuit () != this) { + throw tl::Exception (tl::to_string (tr ("Device not withing given circuit"))); + } + m_devices.erase (device); } void Circuit::add_subcircuit (SubCircuit *subcircuit) { + if (! subcircuit) { + return; + } + if (subcircuit->circuit ()) { + throw tl::Exception (tl::to_string (tr ("Subcircuit already in a circuit"))); + } + subcircuit->set_circuit (this); size_t id = 0; @@ -405,6 +446,13 @@ void Circuit::add_subcircuit (SubCircuit *subcircuit) void Circuit::remove_subcircuit (SubCircuit *subcircuit) { + if (! subcircuit) { + return; + } + if (subcircuit->circuit () != this) { + throw tl::Exception (tl::to_string (tr ("Subcircuit not withing given circuit"))); + } + m_subcircuits.erase (subcircuit); } @@ -420,7 +468,12 @@ void Circuit::unregister_ref (SubCircuit *r) void Circuit::flatten_subcircuit (SubCircuit *subcircuit) { - tl_assert (subcircuit != 0); + if (! subcircuit) { + return; + } + if (subcircuit->circuit () != this) { + throw tl::Exception (tl::to_string (tr ("Subcircuit not withing given circuit"))); + } const db::Circuit *c = subcircuit->circuit_ref (); diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index 91c950468..d816a667e 100644 --- a/src/db/db/dbHash.h +++ b/src/db/db/dbHash.h @@ -283,6 +283,12 @@ namespace std h = hfunc (b, h); h = hfunc (na, h); h = hfunc (nb, h); + } else if (o.size () > 1) { + // iterated array + typename db::array >::iterator i = o.begin (); + while (! (++i).at_end ()) { + h = hfunc (*i, h); + } } if (o.is_complex ()) { diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index f2d4dd92e..41f876b79 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1406,8 +1406,8 @@ private: struct InteractionKeyForClustersType : public InstanceToInstanceInteraction { - InteractionKeyForClustersType (db::cell_index_type _ci1, db::cell_index_type _ci2, const db::ICplxTrans &_t21, const box_type &_box) - : InstanceToInstanceInteraction (_ci1, 0, _ci2, 0, _t21), box (_box) + InteractionKeyForClustersType (db::cell_index_type _ci1, db::cell_index_type _ci2, const db::ICplxTrans &_t1, const db::ICplxTrans &_t21, const box_type &_box) + : InstanceToInstanceInteraction (_ci1, 0, _ci2, 0, _t1, _t21), box (_box) { } bool operator== (const InteractionKeyForClustersType &other) const @@ -1482,18 +1482,22 @@ private: InstanceToInstanceInteraction ii_key; db::ICplxTrans i1t, i2t; + bool fill_cache = false; + + // Cache is only used for single instances, non-iterated, simple or regular arrays. + if ((! i1element.at_end () || i1.size () == 1 || ! i1.is_iterated_array ()) && + (! i2element.at_end () || i2.size () == 1 || ! i2.is_iterated_array ())) { - { i1t = i1element.at_end () ? i1.complex_trans () : i1.complex_trans (*i1element); db::ICplxTrans tt1 = t1 * i1t; i2t = i2element.at_end () ? i2.complex_trans () : i2.complex_trans (*i2element); db::ICplxTrans tt2 = t2 * i2t; - db::ICplxTrans tt21 = tt1.inverted () * tt2; + db::ICplxTrans cache_norm = tt1.inverted (); ii_key = InstanceToInstanceInteraction (i1.cell_index (), (! i1element.at_end () || i1.size () == 1) ? 0 : i1.cell_inst ().delegate (), i2.cell_index (), (! i2element.at_end () || i2.size () == 1) ? 0 : i2.cell_inst ().delegate (), - tt21); + cache_norm, cache_norm * tt2); instance_interaction_cache_type::iterator ii = mp_instance_interaction_cache->find (ii_key); if (ii != mp_instance_interaction_cache->end ()) { @@ -1511,6 +1515,9 @@ private: return; } + + fill_cache = true; + } // array interactions @@ -1611,15 +1618,19 @@ private: // return the list of unique interactions interacting_clusters.insert (interacting_clusters.end (), sorted_interactions.begin (), sorted_interactions.end ()); - // normalize transformations in cache - db::ICplxTrans i1ti = i1t.inverted (), i2ti = i2t.inverted (); - for (std::vector >::iterator i = sorted_interactions.begin (); i != sorted_interactions.end (); ++i) { - i->first.transform (i1ti); - i->second.transform (i2ti); - } + if (fill_cache) { - cluster_instance_pair_list_type &cached = (*mp_instance_interaction_cache) [ii_key]; - cached.insert (cached.end (), sorted_interactions.begin (), sorted_interactions.end ()); + // normalize transformations for cache + db::ICplxTrans i1ti = i1t.inverted (), i2ti = i2t.inverted (); + for (std::vector >::iterator i = sorted_interactions.begin (); i != sorted_interactions.end (); ++i) { + i->first.transform (i1ti); + i->second.transform (i2ti); + } + + cluster_instance_pair_list_type &cached = (*mp_instance_interaction_cache) [ii_key]; + cached.insert (cached.end (), sorted_interactions.begin (), sorted_interactions.end ()); + + } } /** @@ -1641,7 +1652,7 @@ private: box_type common2 = common.transformed (t2i); - InteractionKeyForClustersType ikey (ci1, ci2, t21, common2); + InteractionKeyForClustersType ikey (ci1, ci2, t1i, t21, common2); typename std::map > >::const_iterator ici = m_interaction_cache_for_clusters.find (ikey); if (ici != m_interaction_cache_for_clusters.end ()) { diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 625a972b8..be84db6da 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -762,13 +762,71 @@ inline bool less_array_delegates (const db::ArrayBase *a, const db::ArrayBase *b */ struct InstanceToInstanceInteraction { - InstanceToInstanceInteraction (db::cell_index_type _ci1, const db::ArrayBase *_array1, db::cell_index_type _ci2, const db::ArrayBase *_array2, const db::ICplxTrans &_t21) - : ci1 (_ci1), ci2 (_ci2), array1 (_array1), array2 (_array2), t21 (_t21) - { } + InstanceToInstanceInteraction (db::cell_index_type _ci1, const db::ArrayBase *_array1, db::cell_index_type _ci2, const db::ArrayBase *_array2, const db::ICplxTrans &_tn, const db::ICplxTrans &_t21) + : ci1 (_ci1), ci2 (_ci2), array1 (0), array2 (0), t21 (_t21) + { + if (_array1) { + array1 = _array1->basic_clone (); + static_cast *> (array1)->transform (_tn); + } + + if (_array2) { + array2 = _array2->basic_clone (); + static_cast *> (array2)->transform (_tn); + } + } InstanceToInstanceInteraction () : ci1 (0), ci2 (0), array1 (0), array2 (0) - { } + { + // .. nothing yet .. + } + + InstanceToInstanceInteraction (const InstanceToInstanceInteraction &other) + : ci1 (other.ci1), ci2 (other.ci2), + array1 (other.array1 ? other.array1->basic_clone () : 0), + array2 (other.array2 ? other.array2->basic_clone () : 0), + t21 (other.t21) + { + // .. nothing yet .. + } + + InstanceToInstanceInteraction &operator= (const InstanceToInstanceInteraction &other) + { + if (this != &other) { + + ci1 = other.ci1; + ci2 = other.ci2; + + if (array1) { + delete array1; + } + array1 = other.array1 ? other.array1->basic_clone () : 0; + + if (array2) { + delete array2; + } + array2 = other.array2 ? other.array2->basic_clone () : 0; + + t21 = other.t21; + + } + + return *this; + } + + ~InstanceToInstanceInteraction () + { + if (array1) { + delete array1; + } + array1 = 0; + + if (array2) { + delete array2; + } + array2 = 0; + } bool operator== (const InstanceToInstanceInteraction &other) const { @@ -797,7 +855,7 @@ struct InstanceToInstanceInteraction } db::cell_index_type ci1, ci2; - const db::ArrayBase *array1, *array2; + db::ArrayBase *array1, *array2; db::ICplxTrans t21; }; diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 35424cabc..0b15c3127 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -31,6 +31,9 @@ namespace db static HierarchyBuilderShapeInserter def_inserter; +static HierarchyBuilder::cell_map_type null_map; +static HierarchyBuilder::cell_map_type::const_iterator null_iterator = null_map.end (); + // ------------------------------------------------------------------------------------------- int @@ -168,7 +171,7 @@ HierarchyBuilder::reset () m_cell_map.clear (); m_cells_seen.clear (); m_cell_stack.clear (); - m_cm_entry = cell_map_type::const_iterator (); + m_cm_entry = null_iterator; m_cm_new_entry = false; } @@ -263,14 +266,14 @@ HierarchyBuilder::end (const RecursiveShapeIterator *iter) m_cells_seen.clear (); mp_initial_cell = m_cell_stack.empty () ? 0 : m_cell_stack.front ().second.front (); m_cell_stack.clear (); - m_cm_entry = cell_map_type::const_iterator (); + m_cm_entry = null_iterator; m_cm_new_entry = false; } void HierarchyBuilder::enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { - tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ()); + tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != null_iterator); m_cells_seen.insert (m_cm_entry->first); diff --git a/src/db/db/dbInstances.cc b/src/db/db/dbInstances.cc index 096d515de..4d9a0e6ab 100644 --- a/src/db/db/dbInstances.cc +++ b/src/db/db/dbInstances.cc @@ -241,16 +241,28 @@ Instance::to_string (bool resolve_cell_name) const r = "cell_index=" + tl::to_string (ci.object ().cell_index ()); } - if (ci.is_complex ()) { - r += " " + ci.complex_trans ().to_string (); - } else { - r += " " + (*ci.begin ()).to_string (); - } - db::vector a, b; unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { + + if (ci.is_complex ()) { + r += " " + ci.complex_trans ().to_string (); + } else { + r += " " + (*ci.begin ()).to_string (); + } + r += " array=(" + a.to_string () + "," + b.to_string () + " " + tl::to_string (amax) + "x" + tl::to_string (bmax) + ")"; + + } else { + + for (db::CellInstArray::iterator i = ci.begin (); ! i.at_end (); ++i) { + if (ci.is_complex ()) { + r += " " + ci.complex_trans (*i).to_string (); + } else { + r += " " + (*i).to_string (); + } + } + } if (has_prop_id ()) { @@ -640,13 +652,12 @@ OverlappingInstanceIteratorTraits::init (instance_iteratorbegin_sorted_insts ()), - m_end (insts->end_sorted_insts ()), - mp_insts (insts) + m_end (insts->end_sorted_insts ()) { } cell_index_type diff --git a/src/db/db/dbInstances.h b/src/db/db/dbInstances.h index e754392db..fd2e13a08 100644 --- a/src/db/db/dbInstances.h +++ b/src/db/db/dbInstances.h @@ -945,7 +945,6 @@ public: private: inst_iterator_type m_iter, m_end; - const instances_type *mp_insts; }; /** diff --git a/src/db/db/dbLayoutDiff.cc b/src/db/db/dbLayoutDiff.cc index c3b778abc..f9c29bbce 100644 --- a/src/db/db/dbLayoutDiff.cc +++ b/src/db/db/dbLayoutDiff.cc @@ -1189,6 +1189,8 @@ PrintingDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperti unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { enough (tl::info) << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]" << tl::noendl; + } else if (ci.size () > 1) { + enough (tl::info) << " (+" << (ci.size () - 1) << " irregular locations)" << tl::noendl; } else { enough (tl::info) << "" << tl::noendl; } @@ -1208,6 +1210,8 @@ PrintingDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperti unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { enough (tl::info) << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]" << tl::noendl; + } else if (ci.size () > 1) { + enough (tl::info) << " (+" << (ci.size () - 1) << " irregular locations)" << tl::noendl; } else { enough (tl::info) << "" << tl::noendl; } diff --git a/src/db/db/dbLibrary.h b/src/db/db/dbLibrary.h index 21cd70061..0c45b0c5a 100644 --- a/src/db/db/dbLibrary.h +++ b/src/db/db/dbLibrary.h @@ -238,6 +238,7 @@ namespace tl */ template <> struct type_traits : public type_traits { typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_public_destructor; }; } diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index bd33399d5..dd32a00ab 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -265,6 +265,10 @@ void Netlist::unlock () const tl::vector &Netlist::child_circuits (Circuit *circuit) { + if (circuit->netlist () != this) { + throw tl::Exception (tl::to_string (tr ("Circuit not within given netlist"))); + } + if (! m_valid_topology) { validate_topology (); } @@ -275,6 +279,10 @@ const tl::vector &Netlist::child_circuits (Circuit *circuit) const tl::vector &Netlist::parent_circuits (Circuit *circuit) { + if (circuit->netlist () != this) { + throw tl::Exception (tl::to_string (tr ("Circuit not within given netlist"))); + } + if (! m_valid_topology) { validate_topology (); } @@ -364,18 +372,39 @@ void Netlist::clear () void Netlist::add_circuit (Circuit *circuit) { + if (! circuit) { + return; + } + if (circuit->netlist ()) { + throw tl::Exception (tl::to_string (tr ("Circuit already contained in a netlist"))); + } + m_circuits.push_back (circuit); circuit->set_netlist (this); } void Netlist::remove_circuit (Circuit *circuit) { + if (! circuit) { + return; + } + if (circuit->netlist () != this) { + throw tl::Exception (tl::to_string (tr ("Circuit not within given netlist"))); + } + circuit->set_netlist (0); m_circuits.erase (circuit); } void Netlist::purge_circuit (Circuit *circuit) { + if (! circuit) { + return; + } + if (circuit->netlist () != this) { + throw tl::Exception (tl::to_string (tr ("Circuit not within given netlist"))); + } + circuit->blank (); remove_circuit (circuit); } @@ -406,7 +435,12 @@ void Netlist::flatten_circuits (const std::vector &circuits) void Netlist::flatten_circuit (Circuit *circuit) { - tl_assert (circuit != 0); + if (! circuit) { + return; + } + if (circuit->netlist () != this) { + throw tl::Exception (tl::to_string (tr ("Circuit not within given netlist"))); + } std::vector refs; for (db::Circuit::refs_iterator sc = circuit->begin_refs (); sc != circuit->end_refs (); ++sc) { @@ -448,24 +482,52 @@ DeviceClass *Netlist::device_class_by_name (const std::string &name) void Netlist::add_device_class (DeviceClass *device_class) { + if (! device_class) { + return; + } + if (device_class->netlist ()) { + throw tl::Exception (tl::to_string (tr ("Device class already contained in a netlist"))); + } + m_device_classes.push_back (device_class); device_class->set_netlist (this); } void Netlist::remove_device_class (DeviceClass *device_class) { + if (! device_class) { + return; + } + if (device_class->netlist () != this) { + throw tl::Exception (tl::to_string (tr ("Device class not within given netlist"))); + } + device_class->set_netlist (0); m_device_classes.erase (device_class); } void Netlist::add_device_abstract (DeviceAbstract *device_abstract) { + if (! device_abstract) { + return; + } + if (device_abstract->netlist ()) { + throw tl::Exception (tl::to_string (tr ("Device abstract already contained in a netlist"))); + } + m_device_abstracts.push_back (device_abstract); device_abstract->set_netlist (this); } void Netlist::remove_device_abstract (DeviceAbstract *device_abstract) { + if (! device_abstract) { + return; + } + if (device_abstract->netlist () != this) { + throw tl::Exception (tl::to_string (tr ("Device abstract not within given netlist"))); + } + device_abstract->set_netlist (0); m_device_abstracts.erase (device_abstract); } diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 39be51719..c9103b7c7 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -333,6 +333,63 @@ private: double m_cap_threshold, m_res_threshold; }; +// -------------------------------------------------------------------------------------------------------------------- +// A generic equivalence mapper + +template +class generic_equivalence_tracker +{ +public: + generic_equivalence_tracker () + { + // .. nothing yet .. + } + + bool map (const Obj *a, const Obj *b) + { + std::pair::iterator, bool> inserted1 = m_eq.insert (std::make_pair (a, b)); + tl_assert (inserted1.first->second == b); + std::pair::iterator, bool> inserted2 = m_eq.insert (std::make_pair (b, a)); + tl_assert (inserted2.first->second == a); + return inserted1.second; + } + + void unmap (const Obj *a, const Obj *b) + { + m_eq.erase (a); + m_eq.erase (b); + } + + const Obj *other (const Obj *o) const + { + typename std::map::const_iterator i = m_eq.find (o); + return i == m_eq.end () ? 0 : i->second; + } + +public: + std::map m_eq; +}; + +// -------------------------------------------------------------------------------------------------------------------- +// A class describing the equivalence between subcircuits we established so far + +class SubCircuitEquivalenceTracker + : public generic_equivalence_tracker +{ +public: + SubCircuitEquivalenceTracker () : generic_equivalence_tracker () { } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// A class describing the equivalence between devices we established so far + +class DeviceEquivalenceTracker + : public generic_equivalence_tracker +{ +public: + DeviceEquivalenceTracker () : generic_equivalence_tracker () { } +}; + // -------------------------------------------------------------------------------------------------------------------- // generic_categorizer definition and implementation @@ -559,15 +616,21 @@ class NetGraph; struct CompareData { CompareData () - : other (0), max_depth (0), max_n_branch (0), dont_consider_net_names (false), logger (0), circuit_pin_mapper (0) + : other (0), max_depth (0), max_n_branch (0), depth_first (true), dont_consider_net_names (false), with_ambiguous (false), logger (0), + circuit_pin_mapper (0), subcircuit_equivalence (0), device_equivalence (0), progress (0) { } NetGraph *other; size_t max_depth; size_t max_n_branch; + bool depth_first; bool dont_consider_net_names; + bool with_ambiguous; NetlistCompareLogger *logger; CircuitPinMapper *circuit_pin_mapper; + SubCircuitEquivalenceTracker *subcircuit_equivalence; + DeviceEquivalenceTracker *device_equivalence; + tl::RelativeProgress *progress; }; // -------------------------------------------------------------------------------------------------------------------- @@ -619,6 +682,16 @@ public: m_id2 = pin2_id; } + size_t id1 () const + { + return m_id1; + } + + size_t id2 () const + { + return m_id2; + } + bool operator< (const Transition &other) const { if (is_for_subcircuit () != other.is_for_subcircuit ()) { @@ -888,13 +961,16 @@ namespace db struct CompareNodePtr { - bool operator() (const NetGraphNode *a, const NetGraphNode *b) const + bool operator() (const std::pair &a, const std::pair &b) const { - return *a < *b; + return *a.first < *b.first; } }; class TentativeNodeMapping; +struct NodeRange; +class DeviceMapperForTargetNode; +class SubCircuitMapperForTargetNode; std::string indent (size_t depth) { @@ -1040,20 +1116,16 @@ public: * * If "tentative" is non-null, assignments will be recorded in the TentativeMapping * audit object and can be undone afterwards when backtracking recursion happens. - * - * If "with_ambiguous" is true, ambiguous matches are considered. This will happen - * in a second pass to give exact and unique matches priority over ambiguous matches. */ - size_t derive_node_identities (size_t net_index, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, bool with_ambiguous, CompareData *data); + size_t derive_node_identities (size_t net_index, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, CompareData *data); /** * @brief The backtracking driver * * This method will analyze the given nodes and call "derive_node_identities" for all nodes - * with a proposed identity. With "with_ambiguous", amiguities are resolved by trying - * different combinations in tentative mode and deciding for one combination if possible. + * with a proposed identity. */ - size_t 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); + size_t derive_node_identities_from_node_set (std::vector > &nodes, std::vector > &other_nodes, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, CompareData *data); private: std::vector m_nodes; @@ -1061,7 +1133,9 @@ private: std::map m_net_index; const db::Circuit *mp_circuit; - size_t derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGraphNode::edge_iterator ee, NetGraphNode::edge_iterator e_other, NetGraphNode::edge_iterator ee_other, size_t net_index, size_t other_net_index, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, bool with_ambiguous, CompareData *data); + size_t derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGraphNode::edge_iterator ee, NetGraphNode::edge_iterator e_other, NetGraphNode::edge_iterator ee_other, size_t net_index, size_t other_net_index, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, CompareData *data); + size_t derive_node_identities_from_ambiguity_group (const NodeRange &nr, DeviceMapperForTargetNode &dm, DeviceMapperForTargetNode &dm_other, SubCircuitMapperForTargetNode &scm, SubCircuitMapperForTargetNode &scm_other, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, CompareData *data); + size_t derive_node_identities_from_singular_match (const NetGraphNode *n, const NetGraphNode::edge_iterator &e, const NetGraphNode *n_other, const NetGraphNode::edge_iterator &e_other, DeviceMapperForTargetNode &dm, DeviceMapperForTargetNode &dm_other, SubCircuitMapperForTargetNode &scm, SubCircuitMapperForTargetNode &scm_other, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, CompareData *data, bool consider_net_names); }; // -------------------------------------------------------------------------------------------------------------------- @@ -1426,7 +1500,8 @@ NetGraphNode::edge_equal (const db::Net *a, const db::Net *b) */ struct NodeRange { - NodeRange (size_t _num, std::vector::iterator _n1, std::vector::iterator _nn1, std::vector::iterator _n2, std::vector::iterator _nn2) + NodeRange (size_t _num, std::vector >::iterator _n1, std::vector >::iterator _nn1, + std::vector >::iterator _n2, std::vector >::iterator _nn2) : num (_num), n1 (_n1), nn1 (_nn1), n2 (_n2), nn2 (_nn2) { // .. nothing yet .. @@ -1438,11 +1513,163 @@ struct NodeRange } size_t num; - std::vector::iterator n1, nn1, n2, nn2; + std::vector >::iterator n1, nn1, n2, nn2; }; // -------------------------------------------------------------------------------------------------------------------- +struct CatAndIds +{ +public: + CatAndIds (size_t _cat, size_t _id1, size_t _id2) + : cat (_cat), id1 (_id1), id2 (_id2) + { } + + bool operator== (const CatAndIds &other) const + { + return cat == other.cat && id1 == other.id1 && id2 == other.id2; + } + + bool operator< (const CatAndIds &other) const + { + if (cat != other.cat) { + return cat < other.cat; + } + if (id1 != other.id1) { + return id1 < other.id1; + } + if (id2 != other.id2) { + return id2 < other.id2; + } + return false; + } + + size_t cat, id1, id2; +}; + +template +class generic_mapper_for_target_node +{ +public: + generic_mapper_for_target_node () + { + // .. nothing yet .. + } + + static void derive_mapping (const generic_mapper_for_target_node &m1, const generic_mapper_for_target_node &m2, size_t n1, size_t n2, std::vector > &mapped) + { + if (m1.empty () || m2.empty ()) { + return; + } + + const std::set > &s1 = m1.for_node (n1); + const std::set > &s2 = m2.for_node (n2); + + typename std::set >::const_iterator i1 = s1.begin (), i2 = s2.begin (); + + while (i1 != s1.end () && i2 != s2.end ()) { + + if (i1->first < i2->first) { + ++i1; + } else if (i2->first < i1->first) { + ++i2; + } else { + typename std::set >::const_iterator i10 = i1, i20 = i2; + size_t n1 = 0, n2 = 0; + while (i1 != s1.end () && i1->first == i10->first) { + ++i1; + ++n1; + } + while (i2 != s2.end () && i2->first == i20->first) { + ++i2; + ++n2; + } + if (n1 == 1 && n2 == 1) { + // unique mapping - one device of one category + mapped.push_back (std::make_pair (i10->second, i20->second)); + } + } + + } + + } + +protected: + const std::set > &for_node (size_t ni) const + { + typename std::map > >::const_iterator d = m_per_target_node.find (ni); + tl_assert (d != m_per_target_node.end ()); + return d->second; + } + + std::set > &for_node_nc (size_t ni) + { + return m_per_target_node [ni]; + } + + bool empty () const + { + return m_per_target_node.empty (); + } + +private: + std::map > > m_per_target_node; +}; + +class DeviceMapperForTargetNode + : public generic_mapper_for_target_node +{ +public: + DeviceMapperForTargetNode () + : generic_mapper_for_target_node () + { + // .. nothing yet .. + } + + void insert (const NetGraphNode::edge_type &e) + { + if (e.first.empty ()) { + // happens initially + return; + } + + size_t ni = e.second.first; + std::set > &dev = for_node_nc (ni); + for (std::vector::const_iterator j = e.first.begin (); j != e.first.end (); ++j) { + if (! j->is_for_subcircuit ()) { + dev.insert (std::make_pair (CatAndIds (j->device_pair ().second, j->id1 (), j->id2 ()), j->device_pair ().first)); + } + } + } +}; + +class SubCircuitMapperForTargetNode + : public generic_mapper_for_target_node +{ +public: + SubCircuitMapperForTargetNode () + : generic_mapper_for_target_node () + { + // .. nothing yet .. + } + + void insert (const NetGraphNode::edge_type &e) + { + if (e.first.empty ()) { + // happens initially + return; + } + + size_t ni = e.second.first; + std::set > &sc = for_node_nc (ni); + for (std::vector::const_iterator j = e.first.begin (); j != e.first.end (); ++j) { + if (j->is_for_subcircuit ()) { + sc.insert (std::make_pair (CatAndIds (j->subcircuit_pair ().second, j->id1 (), j->id2 ()), j->subcircuit_pair ().first)); + } + } + } +}; + /** * @brief An audit object which allows reverting tentative node assignments */ @@ -1460,26 +1687,46 @@ public: for (std::vector >::const_iterator i = m_to_undo_to_unknown.begin (); i != m_to_undo_to_unknown.end (); ++i) { i->first->identify (i->second, unknown_id); } + for (std::vector > >::const_iterator i = m_to_undo_devices.begin (); i != m_to_undo_devices.end (); ++i) { + i->first->unmap (i->second.first, i->second.second); + } + for (std::vector > >::const_iterator i = m_to_undo_subcircuits.begin (); i != m_to_undo_subcircuits.end (); ++i) { + i->first->unmap (i->second.first, i->second.second); + } } - static void map_pair (TentativeNodeMapping *nm, NetGraph *g1, size_t n1, NetGraph *g2, size_t n2) + static void map_pair (TentativeNodeMapping *nm, NetGraph *g1, size_t n1, NetGraph *g2, size_t n2, + const DeviceMapperForTargetNode &dm1, const DeviceMapperForTargetNode &dm2, DeviceEquivalenceTracker &device_eq, + const SubCircuitMapperForTargetNode &scm1, const SubCircuitMapperForTargetNode &scm2, SubCircuitEquivalenceTracker &subcircuit_eq, + size_t depth) { g1->identify (n1, n2); g2->identify (n2, n1); + if (nm) { nm->keep (g1, n1); nm->keep (g2, n2); } + + derive_device_equivalence (nm, n1, n2, dm1, dm2, device_eq, depth); + derive_subcircuit_equivalence (nm, n1, n2, scm1, scm2, subcircuit_eq, depth); } - static void map_pair_from_unknown (TentativeNodeMapping *nm, NetGraph *g1, size_t n1, NetGraph *g2, size_t n2) + static void map_pair_from_unknown (TentativeNodeMapping *nm, NetGraph *g1, size_t n1, NetGraph *g2, size_t n2, + const DeviceMapperForTargetNode &dm1, const DeviceMapperForTargetNode &dm2, DeviceEquivalenceTracker &device_eq, + const SubCircuitMapperForTargetNode &scm1, const SubCircuitMapperForTargetNode &scm2, SubCircuitEquivalenceTracker &subcircuit_eq, + size_t depth) { g1->identify (n1, n2); g2->identify (n2, n1); + if (nm) { nm->keep_for_unknown (g1, n1); nm->keep_for_unknown (g2, n2); } + + derive_device_equivalence (nm, n1, n2, dm1, dm2, device_eq, depth); + derive_subcircuit_equivalence (nm, n1, n2, scm1, scm2, subcircuit_eq, depth); } static void map_to_unknown (TentativeNodeMapping *nm, NetGraph *g1, size_t n1) @@ -1490,8 +1737,48 @@ public: } } + static void derive_device_equivalence (TentativeNodeMapping *nm, size_t n1, size_t n2, + const DeviceMapperForTargetNode &dm1, const DeviceMapperForTargetNode &dm2, DeviceEquivalenceTracker &device_eq, size_t depth) + { + std::vector > device_map; + DeviceMapperForTargetNode::derive_mapping (dm1, dm2, n1, n2, device_map); + + for (std::vector >::const_iterator dd = device_map.begin (); dd != device_map.end (); ++dd) { + if (device_eq.map (dd->first, dd->second)) { + if (nm) { + nm->keep (&device_eq, dd->first, dd->second); + } else { + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "enforcing device equivalence: " << dd->first->expanded_name () << " vs. " << dd->second->expanded_name (); + } + } + } + } + } + + static void derive_subcircuit_equivalence (TentativeNodeMapping *nm, size_t n1, size_t n2, + const SubCircuitMapperForTargetNode &scm1, const SubCircuitMapperForTargetNode &scm2, SubCircuitEquivalenceTracker &subcircuit_eq, size_t depth) + { + std::vector > subcircuit_map; + SubCircuitMapperForTargetNode::derive_mapping (scm1, scm2, n1, n2, subcircuit_map); + + for (std::vector >::const_iterator cc = subcircuit_map.begin (); cc != subcircuit_map.end (); ++cc) { + if (subcircuit_eq.map (cc->first, cc->second)) { + if (nm) { + nm->keep (&subcircuit_eq, cc->first, cc->second); + } else { + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "enforcing subcircuit equivalence: " << cc->first->expanded_name () << " vs. " << cc->second->expanded_name (); + } + } + } + } + } + private: std::vector > m_to_undo, m_to_undo_to_unknown; + std::vector > > m_to_undo_devices; + std::vector > > m_to_undo_subcircuits; void keep (NetGraph *g1, size_t n1) { @@ -1502,6 +1789,16 @@ private: { m_to_undo_to_unknown.push_back (std::make_pair (g1, n1)); } + + void keep (DeviceEquivalenceTracker *dt, const db::Device *a, const db::Device *b) + { + m_to_undo_devices.push_back (std::make_pair (dt, std::make_pair (a, b))); + } + + void keep (SubCircuitEquivalenceTracker *dt, const db::SubCircuit *a, const db::SubCircuit *b) + { + m_to_undo_subcircuits.push_back (std::make_pair (dt, std::make_pair (a, b))); + } }; // -------------------------------------------------------------------------------------------------------------------- @@ -1533,8 +1830,6 @@ NetGraph::build (const db::Circuit *c, DeviceCategorizer &device_categorizer, Ci } } - std::sort (m_nodes.begin (), m_nodes.end ()); - for (std::vector::const_iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { m_net_index.insert (std::make_pair (i->net (), i - m_nodes.begin ())); } @@ -1579,110 +1874,174 @@ NetGraph::build (const db::Circuit *c, DeviceCategorizer &device_categorizer, Ci } } -size_t -NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGraphNode::edge_iterator ee, NetGraphNode::edge_iterator e_other, NetGraphNode::edge_iterator ee_other, size_t net_index, size_t other_net_index, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, bool with_ambiguous, CompareData *data) +/** + * @brief Returns true if the edges (given by transition iterators) are compatible with already established device or subcircuit equivalences. + */ +static bool edges_are_compatible (const NetGraphNode::edge_type &e, const NetGraphNode::edge_type &e_other, const DeviceEquivalenceTracker &device_eq, const SubCircuitEquivalenceTracker &sc_eq) { - size_t new_nodes = 0; + std::vector::const_iterator t1 = e.first.begin (), tt1 = e.first.end (); + std::vector::const_iterator t2 = e_other.first.begin (), tt2 = e_other.first.end (); - std::vector nodes; - nodes.reserve (ee - e); + std::vector p1, p2; + + while (t1 != tt1 && t2 != tt2) { + + std::vector::const_iterator t10 = t1, t20 = t2; + + p1.clear (); + while (t1 != tt1 && *t1 == *t10) { + if (t1->is_for_subcircuit ()) { + p1.push_back ((void *) sc_eq.other (t1->subcircuit_pair ().first)); + } else { + p1.push_back ((void *) device_eq.other (t1->device_pair ().first)); + } + ++t1; + } + + p2.clear (); + while (t2 != tt2 && *t2 == *t20) { + if (t2->is_for_subcircuit ()) { + p2.push_back ((void *) (sc_eq.other (t2->subcircuit_pair ().first) ? t2->subcircuit_pair ().first : 0)); + } else { + p2.push_back ((void *) (device_eq.other (t2->device_pair ().first) ? t2->device_pair ().first : 0)); + } + ++t2; + } + + std::sort (p1.begin (), p1.end ()); + std::sort (p2.begin (), p2.end ()); + + if (p1 != p2) { + return false; + } - std::vector other_nodes; - other_nodes.reserve (ee - e); - if (options ()->debug_netcompare) { - tl::info << indent(depth) << "considering transitions:"; } - bool first = true; + tl_assert (t1 == tt1 && t2 == tt2); + return true; +} + +size_t +NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGraphNode::edge_iterator ee, NetGraphNode::edge_iterator e_other, NetGraphNode::edge_iterator ee_other, size_t net_index, size_t other_net_index, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, CompareData *data) +{ + // NOTE: we can skip edges to known nodes because we did a pre-analysis making sure those are compatible + + std::vector > nodes; + nodes.reserve (ee - e); + + std::vector > other_nodes; + other_nodes.reserve (ee - e); + + tl_assert (e->first == e_other->first); for (NetGraphNode::edge_iterator i = e; i != ee; ++i) { if (i->second.first != net_index) { const NetGraphNode *nn = &node (i->second.first); - 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 << ""; + if (! nn->has_other ()) { + nodes.push_back (std::make_pair (nn, i)); } - 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 (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 << ""; + if (! nn->has_other ()) { + other_nodes.push_back (std::make_pair (nn, i)); } - other_nodes.push_back (nn); } } } - if (! nodes.empty () || ! other_nodes.empty ()) { + if (nodes.empty () || other_nodes.empty ()) { + return 0; + } - std::sort (nodes.begin (), nodes.end (), CompareNodePtr ()); - std::sort (other_nodes.begin (), other_nodes.end (), CompareNodePtr ()); + std::sort (nodes.begin (), nodes.end (), CompareNodePtr ()); + std::sort (other_nodes.begin (), other_nodes.end (), CompareNodePtr ()); - // for the purpose of match evaluation we require an exact match of the node structure + size_t new_nodes = 0; - if (tentative) { + if (options ()->debug_netcompare) { - if (nodes.size () != other_nodes.size ()) { - if (options ()->debug_netcompare) { - tl::info << indent(depth) << "=> rejected branch."; - } - return failed_match; + // print transitions if requested + + tl::info << indent(depth) << "considering transitions:"; + + bool first = true; + + for (std::vector >::const_iterator i = nodes.begin (); i != nodes.end (); ++i) { + const NetGraphNode *nn = i->first; + 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->second->first.begin (); t != i->second->first.end(); ++t) { + tl::info << (t != i->second->first.begin () ? "; " : "") << t->to_string() << tl::noendl; + } + tl::info << ""; + } - // 1:1 pairing is less strict - if (nodes.size () > 1 || other_nodes.size () > 1) { - for (size_t i = 0; i < nodes.size (); ++i) { - if (! (*nodes[i] == *other_nodes[i])) { - if (options ()->debug_netcompare) { - tl::info << indent(depth) << "=> rejected branch."; - } - return failed_match; + first = true; + + for (std::vector >::const_iterator i = other_nodes.begin (); i != other_nodes.end (); ++i) { + const NetGraphNode *nn = i->first; + 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->second->first.begin (); t != i->second->first.end(); ++t) { + tl::info << (t != i->second->first.begin () ? "; " : "") << t->to_string() << tl::noendl; + } + tl::info << ""; + } + + } + + // for the purpose of match evaluation we require an exact match of the node structure + + if (tentative) { + + if (nodes.size () != other_nodes.size ()) { + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "=> rejected branch."; + } + return failed_match; + } + + // 1:1 pairing is less strict + if (nodes.size () > 1 || other_nodes.size () > 1) { + for (size_t i = 0; i < nodes.size (); ++i) { + if (! (*nodes[i].first == *other_nodes[i].first)) { + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "=> rejected branch."; } + return failed_match; } } - } - // propagate pairing in picky mode: this means we only accept exact a match if the node set - // is exactly identical and no ambiguous nodes are present when ambiguous nodes are forbidden + } - size_t bt_count = derive_node_identities_from_node_set (nodes, other_nodes, depth, n_branch, tentative, with_ambiguous, data); + // propagate pairing in picky mode: this means we only accept exact a match if the node set + // is exactly identical and no ambiguous nodes are present when ambiguous nodes are forbidden - if (bt_count == failed_match) { - if (tentative) { - if (options ()->debug_netcompare) { - tl::info << indent(depth) << "=> rejected branch."; - } - return bt_count; + size_t bt_count = derive_node_identities_from_node_set (nodes, other_nodes, depth, n_branch, tentative, data); + + if (bt_count == failed_match) { + if (tentative) { + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "=> rejected branch."; } - } else { - new_nodes += bt_count; + return bt_count; } - + } else { + new_nodes += bt_count; } if (options ()->debug_netcompare) { @@ -1707,23 +2066,13 @@ static bool has_subcircuits (db::NetGraphNode::edge_iterator e, db::NetGraphNode } size_t -NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, bool with_ambiguous, CompareData *data) +NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, CompareData *data) { NetGraphNode *n = & node (net_index); size_t other_net_index = n->other_net_index (); NetGraphNode *n_other = & data->other->node (other_net_index); - 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 (); - } - } - - size_t new_nodes = 0; - NetGraphNode nn, nn_other; // If there are subcircuits on the graph we temporarily create edges from our node to the other nodes of @@ -1741,6 +2090,117 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc } + // do a pre-analysis filtering out all nodes with fully satisfied edges or which provide a contradiction + + bool analysis_required = false; + + for (NetGraphNode::edge_iterator e = n->begin (); e != n->end (); ) { + + NetGraphNode::edge_iterator ee = e; + ++ee; + + while (ee != n->end () && ee->first == e->first) { + ++ee; + } + + NetGraphNode::edge_iterator e_other = n_other->find_edge (e->first); + if (e_other != n_other->end ()) { + + NetGraphNode::edge_iterator ee_other = e_other; + ++ee_other; + + while (ee_other != n_other->end () && ee_other->first == e_other->first) { + ++ee_other; + } + + std::vector nodes; + nodes.reserve (ee - e); + + std::vector other_nodes_translated; + other_nodes_translated.reserve (ee - e); + + tl_assert (e->first == e_other->first); + + for (NetGraphNode::edge_iterator i = e; i != ee; ++i) { + if (i->second.first != net_index) { + const NetGraphNode *nn = &node (i->second.first); + if (nn->has_other ()) { + nodes.push_back (nn); + } else { + analysis_required = 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 (nn->has_other ()) { + other_nodes_translated.push_back (&node (nn->other_net_index ())); + } else { + analysis_required = true; + } + } + } + + std::sort (nodes.begin (), nodes.end ()); + std::sort (other_nodes_translated.begin (), other_nodes_translated.end ()); + + // No fit, we can shortcut + if (nodes != other_nodes_translated) { + return tentative ? failed_match : 0; + } + + } else if (tentative) { + // in tentative mode an exact match is required: no having the same edges for a node disqualifies the node + // as matching. + return failed_match; + } + + e = ee; + + } + + if (tentative) { + + // in tentative mode, again an exact match is required + + for (NetGraphNode::edge_iterator e_other = n_other->begin (); e_other != n_other->end (); ) { + + NetGraphNode::edge_iterator ee_other = e_other; + ++ee_other; + + while (ee_other != n_other->end () && ee_other->first == e_other->first) { + ++ee_other; + } + + NetGraphNode::edge_iterator e = n->find_edge (e_other->first); + if (e == n->end ()) { + return failed_match; + } + + e_other = ee_other; + + } + + } + + if (! analysis_required) { + return 0; + } + + // do a detailed analysis + + size_t new_nodes = 0; + + 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 (); + } + } + // non-ambiguous paths to non-assigned nodes create a node identity on the // end of this path @@ -1763,7 +2223,7 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc ++ee_other; } - 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); + size_t bt_count = derive_node_identities_for_edges (e, ee, e_other, ee_other, net_index, other_net_index, depth, n_branch, tentative, data); if (bt_count == failed_match) { if (options ()->debug_netcompare) { tl::info << indent(depth) << "=> rejected pair."; @@ -1773,46 +2233,12 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc new_nodes += bt_count; } - } 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 (options ()->debug_netcompare) { - tl::info << indent(depth) << "=> rejected pair for missing edge."; - } - return failed_match; } e = ee; } - if (tentative) { - - // in tentative mode, again an exact match is required - - for (NetGraphNode::edge_iterator e_other = n_other->begin (); e_other != n_other->end (); ) { - - NetGraphNode::edge_iterator ee_other = e_other; - ++ee_other; - - while (ee_other != n_other->end () && ee_other->first == e_other->first) { - ++ee_other; - } - - NetGraphNode::edge_iterator e = n->find_edge (e_other->first); - if (e == n->end ()) { - if (options ()->debug_netcompare) { - tl::info << indent(depth) << "=> rejected pair for missing edge."; - } - return failed_match; - } - - e_other = ee_other; - - } - - } - 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"; @@ -1827,26 +2253,26 @@ namespace { struct SortNodeByNet { public: - bool operator() (const NetGraphNode *a, const NetGraphNode *b) const + bool operator() (const std::pair &a, const std::pairb) const { - tl_assert (a->net () && b->net ()); - return name_compare (a->net ()->name (), b->net ()->name ()) < 0; + tl_assert (a.first->net () && b.first->net ()); + return name_compare (a.first->net ()->name (), b.first->net ()->name ()) < 0; } }; } -static void sort_node_range_by_best_match (NodeRange &nr) +static void sort_node_range_by_best_match (const NodeRange &nr) { std::stable_sort (nr.n1, nr.nn1, SortNodeByNet ()); std::stable_sort (nr.n2, nr.nn2, SortNodeByNet ()); - std::vector nomatch1, nomatch2; + std::vector > nomatch1, nomatch2; nomatch1.reserve (nr.nn1 - nr.n1); nomatch2.reserve (nr.nn2 - nr.n2); - std::vector::const_iterator i = nr.n1, j = nr.n2; - std::vector::iterator iw = nr.n1, jw = nr.n2; + std::vector >::const_iterator i = nr.n1, j = nr.n2; + std::vector >::iterator iw = nr.n1, jw = nr.n2; SortNodeByNet compare; @@ -1895,9 +2321,20 @@ 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) +static bool net_names_are_equal (const db::Net *a, const db::Net *b) { + if (! a || ! b || a->name ().empty () || b->name ().empty ()) { + return false; + } else { + return name_compare (a->name (), b->name ()) == 0; + } +} + +size_t +NetGraph::derive_node_identities_from_ambiguity_group (const NodeRange &nr, DeviceMapperForTargetNode &dm, DeviceMapperForTargetNode &dm_other, SubCircuitMapperForTargetNode &scm, SubCircuitMapperForTargetNode &scm_other, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, CompareData *data) +{ + tl::AbsoluteProgress progress (tl::to_string (tr ("Deriving match for ambiguous net group"))); + std::string indent_s; if (options ()->debug_netcompare) { indent_s = indent (depth); @@ -1905,6 +2342,294 @@ NetGraph::derive_node_identities_from_node_set (std::vector > pairs; + tl::equivalence_clusters equivalent_other_nodes; + + sort_node_range_by_best_match (nr); + + { + + // marks the nodes from the ambiguity group as unknown so we don't revisit them (causing deep recursion) + TentativeNodeMapping tn2unknown; + + // collect and mark the ambiguity combinations to consider + std::vector >::const_iterator> iters1, iters2; + + for (std::vector >::const_iterator i1 = nr.n1; i1 != nr.nn1; ++i1) { + if (! i1->first->has_any_other ()) { + iters1.push_back (i1); + size_t ni = node_index_for_net (i1->first->net ()); + TentativeNodeMapping::map_to_unknown (&tn2unknown, this, ni); + } + } + + for (std::vector >::const_iterator i2 = nr.n2; i2 != nr.nn2; ++i2) { + if (! i2->first->has_any_other ()) { + iters2.push_back (i2); + size_t other_ni = data->other->node_index_for_net (i2->first->net ()); + TentativeNodeMapping::map_to_unknown (&tn2unknown, data->other, other_ni); + } + } + + for (std::vector >::const_iterator>::const_iterator ii1 = iters1.begin (); ii1 != iters1.end (); ++ii1) { + + std::vector >::const_iterator i1 = *ii1; + + bool any = false; + std::vector >::const_iterator>::iterator to_remove = iters2.end (); + + for (std::vector >::const_iterator>::iterator ii2 = iters2.begin (); ii2 != iters2.end (); ++ii2) { + + ++progress; + + std::vector >::const_iterator i2 = *ii2; + + // try this candidate in tentative mode + if (options ()->debug_netcompare) { + tl::info << indent_s << "trying in tentative mode: " << i1->first->net ()->expanded_name () << " vs. " << i2->first->net ()->expanded_name (); + } + + if (! edges_are_compatible (*i1->second, *i2->second, *data->device_equivalence, *data->subcircuit_equivalence)) { + if (options ()->debug_netcompare) { + tl::info << indent_s << "=> rejected because edges are incompatible with already established device or subcircuit equivalences"; + } + continue; + } + + if (! data->dont_consider_net_names && net_names_are_equal (i1->first->net (), i2->first->net ())) { + + if (options ()->debug_netcompare) { + tl::info << indent_s << "=> accepted for identical names"; + } + + // utilize net names to propose a match + new_nodes += 1; + pairs.push_back (std::make_pair (i1->first, i2->first)); + to_remove = ii2; + any = true; + break; + + } else { + + size_t ni = node_index_for_net (i1->first->net ()); + size_t other_ni = data->other->node_index_for_net (i2->first->net ()); + + TentativeNodeMapping tn; + TentativeNodeMapping::map_pair_from_unknown (&tn, this, ni, data->other, other_ni, dm, dm_other, *data->device_equivalence, scm, scm_other, *data->subcircuit_equivalence, depth); + + size_t bt_count = derive_node_identities (ni, depth + 1, complexity * n_branch, &tn, data); + + if (bt_count != failed_match) { + + if (options ()->debug_netcompare) { + tl::info << indent_s << "match found"; + } + // we have a match ... + + if (any) { + + // there is already a known pair, so we can mark *i2 and the previous *i2 as equivalent + // (makes them ambiguous) + equivalent_other_nodes.same (i2->first, pairs.back ().second); + // we know enough now ... + break; + + } else { + + // identified a new pair + new_nodes += bt_count + 1; + pairs.push_back (std::make_pair (i1->first, i2->first)); + to_remove = ii2; + any = true; + + // no ambiguity analysis in tentative mode - we can stop now + if (tentative) { + break; + } + + } + + } + + } + + } + + if (to_remove != iters2.end ()) { + iters2.erase (to_remove); + } + + if (! any && tentative) { + if (options ()->debug_netcompare) { + tl::info << indent_s << "mismatch."; + } + // a mismatch - stop here. + return failed_match; + } + + } + + } + + if (! tentative) { + + // issue the matching pairs + + for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { + + size_t ni = node_index_for_net (p->first->net ()); + size_t other_ni = data->other->node_index_for_net (p->second->net ()); + + TentativeNodeMapping::map_pair (0, this, ni, data->other, other_ni, dm, dm_other, *data->device_equivalence, scm, scm_other, *data->subcircuit_equivalence, depth); + + 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 (); + } + } + + if (data->logger) { + bool ambiguous = equivalent_other_nodes.has_attribute (p->second); + if (ambiguous) { + data->logger->match_ambiguous_nets (p->first->net (), p->second->net ()); + } else { + data->logger->match_nets (p->first->net (), p->second->net ()); + } + } + + ++*data->progress; + + } + + // And seek further from these pairs + + for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { + + size_t ni = node_index_for_net (p->first->net ()); + + size_t bt_count = derive_node_identities (ni, depth + 1, complexity * n_branch, tentative, data); + tl_assert (bt_count != failed_match); + + } + + } else { + + for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { + + size_t ni = node_index_for_net (p->first->net ()); + size_t other_ni = data->other->node_index_for_net (p->second->net ()); + + TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni, dm, dm_other, *data->device_equivalence, scm, scm_other, *data->subcircuit_equivalence, depth); + + } + + } + + return new_nodes; +} + +size_t +NetGraph::derive_node_identities_from_singular_match (const NetGraphNode *n, const NetGraphNode::edge_iterator &e, const NetGraphNode *n_other, const NetGraphNode::edge_iterator &e_other, DeviceMapperForTargetNode &dm, DeviceMapperForTargetNode &dm_other, SubCircuitMapperForTargetNode &scm, SubCircuitMapperForTargetNode &scm_other, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, CompareData *data, bool consider_net_names) +{ + std::string indent_s; + if (options ()->debug_netcompare) { + indent_s = indent (depth); + indent_s += "*" + tl::to_string (n_branch) + " "; + } + + if (! edges_are_compatible (*e, *e_other, *data->device_equivalence, *data->subcircuit_equivalence)) { + + if (options ()->debug_netcompare) { + tl::info << indent(depth) << "=> rejected because edges are incompatible with already established device or subcircuit equivalences"; + } + return tentative ? failed_match : 0; + + } else if (! n->has_any_other () && ! n_other->has_any_other ()) { + + // in tentative mode, reject this choice if both nets are named and + // their names differ -> this favors net matching by name + + if (tentative && consider_net_names && net_names_are_different (n->net (), n_other->net ())) { + if (options ()->debug_netcompare) { + tl::info << indent_s << "rejecting pair as names are not identical: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name (); + } + return failed_match; + } + + // A single candiate: just take this one -> this may render + // inexact matches, but further propagates net pairing + + size_t ni = node_index_for_net (n->net ()); + size_t other_ni = data->other->node_index_for_net (n_other->net ()); + + TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni, dm, dm_other, *data->device_equivalence, scm, scm_other, *data->subcircuit_equivalence, depth); + + if (options ()->debug_netcompare) { + tl::info << indent_s << "deduced match (singular): " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name (); + } + if (! tentative) { + ++*data->progress; + if (data->logger) { + if (! (node (ni) == data->other->node (other_ni))) { + // this is a mismatch, but we continue with this + data->logger->net_mismatch (n->net (), n_other->net ()); + } else { + data->logger->match_nets (n->net (), n_other->net ()); + } + } + } + + size_t new_nodes = 1; + + if (data->depth_first || tentative) { + size_t bt_count = derive_node_identities (ni, depth + 1, n_branch, tentative, data); + if (bt_count == failed_match) { + if (tentative) { + return failed_match; + } + } else { + new_nodes += bt_count; + } + } + + return new_nodes; + + } else if (n->has_unknown_other ()) { + + // accept any other net + return 0; + + } else if (n->has_other ()) { + + // this decision leads to a contradiction + if (data->other->node_index_for_net (n_other->net ()) != n->other_net_index ()) { + return failed_match; + } else { + return 0; + } + + } else { + + // mismatch of assignment state + return failed_match; + + } +} + +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, CompareData *data) +{ + std::string indent_s; + if (options ()->debug_netcompare) { + indent_s = indent (depth); + indent_s += "*" + tl::to_string (n_branch) + " "; + } if (depth > data->max_depth) { if (options ()->debug_netcompare) { @@ -1913,99 +2638,64 @@ NetGraph::derive_node_identities_from_node_set (std::vector >::const_iterator i = nodes.begin (); i != nodes.end (); ++i) { + dm.insert (*i->second); + scm.insert (*i->second); + } + + DeviceMapperForTargetNode dm_other; + SubCircuitMapperForTargetNode scm_other; + for (std::vector >::const_iterator i = other_nodes.begin (); i != other_nodes.end (); ++i) { + dm_other.insert (*i->second); + scm_other.insert (*i->second); + } + if (nodes.size () == 1 && other_nodes.size () == 1) { - if (! nodes.front ()->has_any_other () && ! other_nodes.front ()->has_any_other ()) { - - // a single candiate: just take this one -> this may render - // inexact matches, but further propagates net pairing - - size_t ni = node_index_for_net (nodes.front ()->net ()); - size_t other_ni = data->other->node_index_for_net (other_nodes.front ()->net ()); - - TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni); - - 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 - data->logger->net_mismatch (nodes.front ()->net (), other_nodes.front ()->net ()); - } else { - data->logger->match_nets (nodes.front ()->net (), other_nodes.front ()->net ()); - } - } - - // continue here. - size_t bt_count = derive_node_identities (ni, depth + 1, n_branch, tentative, with_ambiguous, data); - - if (bt_count != failed_match) { - new_nodes += bt_count; - } else if (tentative) { - return bt_count; - } - - new_nodes += 1; - - } else if (nodes.front ()->has_unknown_other ()) { - - // accept this solution as the pairing is possible - - } else if (nodes.front ()->has_other ()) { - - // this decision leads to a contradiction - if (data->other->node_index_for_net (other_nodes.front ()->net ()) != nodes.front ()->other_net_index ()) { - return failed_match; - } - - } else { - - // mismatch of assignment state - return failed_match; - - } - - return new_nodes; + return derive_node_identities_from_singular_match (nodes.front ().first, nodes.front ().second, other_nodes.front ().first, other_nodes.front ().second, + dm, dm_other, scm, scm_other, depth, n_branch, tentative, data, false /*don't consider net names*/); } // Determine the range of nodes with same identity std::vector node_ranges; + size_t new_nodes = 0; - std::vector::iterator n1 = nodes.begin (); - std::vector::iterator n2 = other_nodes.begin (); + std::vector >::iterator n1 = nodes.begin (); + std::vector >::iterator n2 = other_nodes.begin (); while (n1 != nodes.end () && n2 != other_nodes.end ()) { - if ((*n1)->has_any_other ()) { + if (n1->first->has_other ()) { ++n1; continue; - } else if ((*n2)->has_any_other ()) { + } else if (n2->first->has_other ()) { ++n2; continue; } - if (**n1 < **n2) { + if (*n1->first < *n2->first) { ++n1; continue; - } else if (**n2 < **n1) { + } else if (*n2->first < *n1->first) { ++n2; continue; } - std::vector::iterator nn1 = n1, nn2 = n2; + std::vector >::iterator nn1 = n1, nn2 = n2; size_t num = 1; ++nn1; ++nn2; while (nn1 != nodes.end () && nn2 != other_nodes.end ()) { - if ((*nn1)->has_any_other ()) { + if (nn1->first->has_other ()) { ++nn1; - } else if ((*nn2)->has_any_other ()) { + } else if (nn2->first->has_other ()) { ++nn2; - } else if (! (**nn1 == **n1) || ! (**nn2 == **n2)) { + } else if (! (*nn1->first == *n1->first) || ! (*nn2->first == *n2->first)) { break; } else { ++num; @@ -2014,13 +2704,13 @@ NetGraph::derive_node_identities_from_node_set (std::vectorwith_ambiguous) { node_ranges.push_back (NodeRange (num, n1, nn1, n2, nn2)); } // in tentative mode ambiguous nodes don't make a match without // with_ambiguous - if (num > 1 && tentative && ! with_ambiguous) { + if (num > 1 && tentative && ! data->with_ambiguous) { return failed_match; } @@ -2029,7 +2719,7 @@ NetGraph::derive_node_identities_from_node_set (std::vectorwith_ambiguous) { std::stable_sort (node_ranges.begin (), node_ranges.end ()); } @@ -2038,9 +2728,9 @@ NetGraph::derive_node_identities_from_node_set (std::vectorn1 != nr->nn1 && nr->n2 != nr->nn2) { - if ((*nr->n1)->has_any_other ()) { + if (nr->n1->first->has_other ()) { ++nr->n1; - } else if ((*nr->n2)->has_any_other ()) { + } else if (nr->n2->first->has_other ()) { ++nr->n2; } else { break; @@ -2048,12 +2738,12 @@ NetGraph::derive_node_identities_from_node_set (std::vectornum = 0; - std::vector::const_iterator i1 = nr->n1, i2 = nr->n2; + std::vector >::const_iterator i1 = nr->n1, i2 = nr->n2; while (i1 != nr->nn1 && i2 != nr->nn2) { - if ((*i1)->has_any_other ()) { + if (i1->first->has_other ()) { ++i1; - } else if ((*i2)->has_any_other ()) { + } else if (i2->first->has_other ()) { ++i2; } else { ++nr->num; @@ -2068,66 +2758,13 @@ NetGraph::derive_node_identities_from_node_set (std::vectornum == 1) { - if (! (*nr->n1)->has_any_other () && ! (*nr->n2)->has_any_other ()) { - - // in tentative mode, reject this choice if both nets are named and - // 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 (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; - } - - // A single candiate: just take this one -> this may render - // inexact matches, but further propagates net pairing - - size_t ni = node_index_for_net ((*nr->n1)->net ()); - size_t other_ni = data->other->node_index_for_net ((*nr->n2)->net ()); - - TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni); - - 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 - data->logger->net_mismatch ((*nr->n1)->net (), (*nr->n2)->net ()); - } else { - data->logger->match_nets ((*nr->n1)->net (), (*nr->n2)->net ()); - } - } - - // continue here. - size_t bt_count = derive_node_identities (ni, depth + 1, n_branch, tentative, with_ambiguous, data); - - if (bt_count != failed_match) { - new_nodes += bt_count; - new_nodes += 1; - } else if (tentative) { - new_nodes = bt_count; - } - - } else if ((*nr->n1)->has_unknown_other ()) { - - // accept any other net - - } else if ((*nr->n1)->has_other ()) { - - // this decision leads to a contradiction - if (data->other->node_index_for_net ((*nr->n2)->net ()) != (*nr->n1)->other_net_index ()) { - return failed_match; - } - - } else { - - // mismatch of assignment state + size_t n = derive_node_identities_from_singular_match (nr->n1->first, nr->n1->second, nr->n2->first, nr->n2->second, dm, dm_other, scm, scm_other, depth, n_branch, tentative, data, ! data->dont_consider_net_names); + if (n == failed_match) { return failed_match; - } + new_nodes += n; + } else if (nr->num * n_branch > data->max_n_branch) { if (options ()->debug_netcompare) { @@ -2141,157 +2778,12 @@ NetGraph::derive_node_identities_from_node_set (std::vectornum << " members"; } - // sort the ambiguity group such that net names match best - - std::vector > pairs; - tl::equivalence_clusters equivalent_other_nodes; - std::set seen; - - if (! data->dont_consider_net_names) { - sort_node_range_by_best_match (*nr); + size_t n = derive_node_identities_from_ambiguity_group (*nr, dm, dm_other, scm, scm_other, depth, n_branch, tentative, data); + if (n == failed_match) { + return failed_match; } - { - - // marks the nodes from the ambiguity group as unknown so we don't revisit them (causing deep recursion) - TentativeNodeMapping tn2unknown; - - // collect and mark the ambiguity combinations to consider - std::vector::const_iterator> iters1, iters2; - - for (std::vector::const_iterator i1 = nr->n1; i1 != nr->nn1; ++i1) { - if (! (*i1)->has_any_other ()) { - iters1.push_back (i1); - size_t ni = node_index_for_net ((*i1)->net ()); - TentativeNodeMapping::map_to_unknown (&tn2unknown, this, ni); - } - } - - for (std::vector::const_iterator i2 = nr->n2; i2 != nr->nn2; ++i2) { - if (! (*i2)->has_any_other ()) { - iters2.push_back (i2); - size_t other_ni = data->other->node_index_for_net ((*i2)->net ()); - TentativeNodeMapping::map_to_unknown (&tn2unknown, data->other, other_ni); - } - } - - for (std::vector::const_iterator>::const_iterator ii1 = iters1.begin (); ii1 != iters1.end (); ++ii1) { - - std::vector::const_iterator i1 = *ii1; - - bool any = false; - - for (std::vector::const_iterator>::const_iterator ii2 = iters2.begin (); ii2 != iters2.end (); ++ii2) { - - std::vector::const_iterator i2 = *ii2; - - if (seen.find (*i2) != seen.end ()) { - continue; - } - - size_t ni = node_index_for_net ((*i1)->net ()); - size_t other_ni = data->other->node_index_for_net ((*i2)->net ()); - - TentativeNodeMapping tn; - TentativeNodeMapping::map_pair_from_unknown (&tn, this, ni, data->other, other_ni); - - // try this candidate in tentative mode - 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 (options ()->debug_netcompare) { - tl::info << indent_s << "match found"; - } - // we have a match ... - - if (any) { - - // there is already a known pair, so we can mark *i2 and the previous *i2 as equivalent - // (makes them ambiguous) - equivalent_other_nodes.same (*i2, pairs.back ().second); - - } else { - - // identified a new pair - new_nodes += bt_count + 1; - pairs.push_back (std::make_pair (*i1, *i2)); - seen.insert (*i2); - any = true; - - } - - } - - } - - if (! any && tentative) { - if (options ()->debug_netcompare) { - tl::info << indent_s << "mismatch."; - } - // a mismatch - stop here. - return failed_match; - } - - } - - } - - if (! tentative) { - - // issue the matching pairs - - for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { - - size_t ni = node_index_for_net (p->first->net ()); - size_t other_ni = data->other->node_index_for_net (p->second->net ()); - - TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni); - - 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 (); - } - } - if (data->logger) { - bool ambiguous = equivalent_other_nodes.has_attribute (p->second); - if (ambiguous) { - data->logger->match_ambiguous_nets (p->first->net (), p->second->net ()); - } else { - data->logger->match_nets (p->first->net (), p->second->net ()); - } - } - - } - - // And seek further from these pairs - - for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { - - size_t ni = node_index_for_net (p->first->net ()); - - size_t bt_count = derive_node_identities (ni, depth + 1, nr->num * n_branch, tentative, with_ambiguous, data); - tl_assert (bt_count != failed_match); - - } - - } else { - - for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { - - size_t ni = node_index_for_net (p->first->net ()); - size_t other_ni = data->other->node_index_for_net (p->second->net ()); - - TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni); - - } - - } + new_nodes += n; if (options ()->debug_netcompare) { tl::info << indent_s << "finished analysis of ambiguity group with " << nr->num << " members"; @@ -2320,6 +2812,7 @@ NetlistComparer::NetlistComparer (NetlistCompareLogger *logger) m_max_depth = 50; m_max_n_branch = 500; + m_depth_first = true; m_dont_consider_net_names = false; } @@ -2531,6 +3024,8 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const std::map c12_pin_mapping, c22_pin_mapping; + tl::RelativeProgress progress (tl::to_string (tr ("Comparing netlists")), a->circuit_count (), 1); + for (db::Netlist::const_bottom_up_circuit_iterator c = a->begin_bottom_up (); c != a->end_bottom_up (); ++c) { const db::Circuit *ca = c.operator-> (); @@ -2561,6 +3056,7 @@ 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 (options ()->debug_netcompare) { + tl::info << "----------------------------------------------------------------------"; tl::info << "treating circuit: " << ca->name () << " vs. " << cb->name (); } if (mp_logger) { @@ -2593,6 +3089,8 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const } + ++progress; + } if (mp_logger) { @@ -2663,8 +3161,42 @@ compute_device_key (const db::Device &device, const db::NetGraph &g, bool strict k.push_back (std::make_pair (terminal_id, net_id)); } - std::sort (k.begin (), k.end ()); + return k; +} +static std::vector > +compute_device_key_for_this (const db::Device &device, const db::NetGraph &g, bool strict, bool &mapped) +{ + std::vector > k = compute_device_key (device, g, strict); + + mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { + if (! g.begin () [i->second].has_other ()) { + i->second = invalid_id; // normalization + mapped = false; + } + } + + std::sort (k.begin (), k.end ()); + return k; +} + +static std::vector > +compute_device_key_for_other (const db::Device &device, const db::NetGraph &g, bool strict, bool &mapped) +{ + std::vector > k = compute_device_key (device, g, strict); + + mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { + if (! g.begin () [i->second].has_other ()) { + i->second = invalid_id; // normalization + mapped = false; + } else { + i->second = g.begin () [i->second].other_net_index (); + } + } + + std::sort (k.begin (), k.end ()); return k; } @@ -2699,11 +3231,51 @@ compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetGraph &g, } - std::sort (k.begin (), k.end ()); - return true; } +static std::vector > +compute_subcircuit_key_for_this (const db::SubCircuit &subcircuit, const db::NetGraph &g, const std::map *circuit_map, const CircuitPinMapper *pin_map, bool &mapped, bool &valid) +{ + valid = true; + std::vector > k; + if (! compute_subcircuit_key (subcircuit, g, circuit_map, pin_map, k)) { + valid = false; + } + + mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { + if (! g.begin () [i->second].has_other ()) { + mapped = false; + } + } + + std::sort (k.begin (), k.end ()); + return k; +} + +static std::vector > +compute_subcircuit_key_for_other (const db::SubCircuit &subcircuit, const db::NetGraph &g, const std::map *circuit_map, const CircuitPinMapper *pin_map, bool &mapped, bool &valid) +{ + valid = true; + std::vector > k; + if (! compute_subcircuit_key (subcircuit, g, circuit_map, pin_map, k)) { + valid = false; + } + + mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { + if (! g.begin () [i->second].has_other ()) { + mapped = false; + } else { + i->second = g.begin () [i->second].other_net_index (); + } + } + + std::sort (k.begin (), k.end ()); + return k; +} + namespace { inline double size_dist (size_t a, size_t b) @@ -2790,26 +3362,20 @@ void align (Iter i1, Iter i2, Iter j1, Iter j2, Distance distance) vj.push_back (j); } - while (vi.size () < vj.size ()) { - vi.push_back (Iter ()); - } + size_t sz = std::max (vi.size (), vj.size ()); - while (vj.size () < vi.size ()) { - vj.push_back (Iter ()); - } - - if (vi.size () <= 1) { + if (sz <= 1) { return; } // Caution: this is an O(2) algorithm ... bool any_swapped = true; - for (size_t n = 0; n < vi.size () - 1 && any_swapped; ++n) { + for (size_t n = 0; n < sz - 1 && any_swapped; ++n) { any_swapped = false; - for (size_t m = n + 1; m < vj.size (); ++m) { - if (vi [n] == Iter () || vi [m] == Iter () || vj [n] == Iter () || vj [m] == Iter ()) { + for (size_t m = n + 1; m < sz; ++m) { + if (n >= vi.size () || m >= vi.size () || n >= vj.size () || m >= vj.size ()) { continue; } else if (distance (*vi [n], *vj [m]) + distance (*vi [m], *vj [n]) < distance (*vi [n], *vj [n]) + distance (*vi [m], *vj [m])) { // this will reduce the overall distance: @@ -2860,6 +3426,8 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, std::map &c22_circuit_and_pin_mapping) const { db::DeviceFilter device_filter (m_cap_threshold, m_res_threshold); + SubCircuitEquivalenceTracker subcircuit_equivalence; + DeviceEquivalenceTracker device_equivalence; db::NetGraph g1, g2; @@ -2881,11 +3449,13 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, int iter = 0; + tl::RelativeProgress progress (tl::to_string (tr ("Comparing circuits ")) + c1->name () + "/" + c2->name (), std::max (g1.end () - g1.begin (), g2.end () - g2.begin ()), 1); + // two passes: one without ambiguities, the second one with - bool good; + bool good = false; - for (int pass = 0; pass < 2; ++pass) { + for (int pass = 0; pass < 2 && ! good; ++pass) { if (options ()->debug_netcompare) { if (pass > 0) { @@ -2912,11 +3482,16 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, data.other = &g2; data.max_depth = m_max_depth; data.max_n_branch = m_max_n_branch; + data.depth_first = m_depth_first; data.dont_consider_net_names = m_dont_consider_net_names; + data.with_ambiguous = (pass > 0); data.circuit_pin_mapper = &circuit_pin_mapper; + data.subcircuit_equivalence = &subcircuit_equivalence; + data.device_equivalence = &device_equivalence; data.logger = mp_logger; + data.progress = &progress; - size_t ni = g1.derive_node_identities (i1 - g1.begin (), 0, 1, 0 /*not tentative*/, pass > 0 /*with ambiguities*/, &data); + size_t ni = g1.derive_node_identities (i1 - g1.begin (), 0, 1, 0 /*not tentative*/, &data); if (ni > 0 && ni != failed_match) { new_identities += ni; if (options ()->debug_netcompare) { @@ -2934,29 +3509,35 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, // derive new identities through topology: first collect all nets with the same topological signature - std::vector nodes, other_nodes; + std::vector > nodes, other_nodes; + + std::vector no_edges; + no_edges.push_back (NetGraphNode::edge_type ()); nodes.reserve (g1.end () - g1.begin ()); for (db::NetGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { if (! i1->has_other () && i1->net ()) { - nodes.push_back (i1.operator-> ()); + nodes.push_back (std::make_pair (i1.operator-> (), no_edges.begin ())); } } other_nodes.reserve (g2.end () - g2.begin ()); for (db::NetGraph::node_iterator i2 = g2.begin (); i2 != g2.end (); ++i2) { if (! i2->has_other () && i2->net ()) { - other_nodes.push_back (i2.operator-> ()); + other_nodes.push_back (std::make_pair (i2.operator-> (), no_edges.begin ())); } } if (nodes.empty () || other_nodes.empty ()) { // active mismatched nodes give an error - for (std::vector::const_iterator n = nodes.begin (); n != nodes.end () && good; ++n) { - good = is_passive_net ((*n)->net (), c12_circuit_and_pin_mapping); + for (std::vector >::const_iterator n = nodes.begin (); n != nodes.end () && good; ++n) { + good = is_passive_net (n->first->net (), c12_circuit_and_pin_mapping); } - for (std::vector::const_iterator n = other_nodes.begin (); n != other_nodes.end () && good; ++n) { - good = is_passive_net ((*n)->net (), c22_circuit_and_pin_mapping); + for (std::vector >::const_iterator n = other_nodes.begin (); n != other_nodes.end () && good; ++n) { + good = is_passive_net (n->first->net (), c22_circuit_and_pin_mapping); + } + if (options ()->debug_netcompare) { + tl::info << "Stopped with " << nodes.size () << "/" << other_nodes.size () << " nodes left unresolved " << (good ? "(accepted)" : "(not accepted)"); } // this assumes that we don't gain anything here. Stop now. break; @@ -2970,10 +3551,14 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, data.max_depth = m_max_depth; data.max_n_branch = m_max_n_branch; data.dont_consider_net_names = m_dont_consider_net_names; + data.with_ambiguous = (pass > 0); data.circuit_pin_mapper = &circuit_pin_mapper; + data.subcircuit_equivalence = &subcircuit_equivalence; + data.device_equivalence = &device_equivalence; data.logger = mp_logger; + data.progress = &progress; - size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, 0, 1, 0 /*not tentative*/, pass > 0 /*with ambiguities*/, &data); + size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, 0, 1, 0 /*not tentatively*/, &data); if (ni > 0 && ni != failed_match) { new_identities += ni; if (options ()->debug_netcompare) { @@ -2982,6 +3567,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } if (new_identities == 0) { + if (options ()->debug_netcompare) { + tl::info << "Stopped with " << nodes.size () << "/" << other_nodes.size () << " nodes left unresolved."; + } good = false; break; } @@ -2994,6 +3582,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::NetGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { if (! i->has_other ()) { + if (options ()->debug_netcompare) { + tl::info << "Unresolved net from left: " << i->net ()->expanded_name () << " " << (good ? "(accepted)" : "(not accepted)"); + } if (mp_logger) { if (good) { mp_logger->match_nets (i->net (), 0); @@ -3010,6 +3601,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::NetGraph::node_iterator i = g2.begin (); i != g2.end (); ++i) { if (! i->has_other ()) { + if (options ()->debug_netcompare) { + tl::info << "Unresolved net from right: " << i->net ()->expanded_name () << " " << (good ? "(accepted)" : "(not accepted)"); + } if (mp_logger) { if (good) { mp_logger->match_nets (0, i->net ()); @@ -3025,8 +3619,8 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } do_pin_assignment (c1, g1, c2, g2, c12_circuit_and_pin_mapping, c22_circuit_and_pin_mapping, pin_mismatch, good); - do_device_assignment (c1, g1, c2, g2, device_filter, device_categorizer, good); - do_subcircuit_assignment (c1, g1, c2, g2, circuit_categorizer, circuit_pin_mapper, c12_circuit_and_pin_mapping, c22_circuit_and_pin_mapping, good); + do_device_assignment (c1, g1, c2, g2, device_filter, device_categorizer, device_equivalence, good); + do_subcircuit_assignment (c1, g1, c2, g2, circuit_categorizer, circuit_pin_mapper, c12_circuit_and_pin_mapping, c22_circuit_and_pin_mapping, subcircuit_equivalence, good); return good; } @@ -3241,7 +3835,7 @@ NetlistComparer::do_pin_assignment (const db::Circuit *c1, const db::NetGraph &g } void -NetlistComparer::do_device_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, const db::DeviceFilter &device_filter, db::DeviceCategorizer &device_categorizer, bool &good) const +NetlistComparer::do_device_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, const db::DeviceFilter &device_filter, db::DeviceCategorizer &device_categorizer, DeviceEquivalenceTracker &device_eq, bool &good) const { // Report device assignment @@ -3250,27 +3844,26 @@ NetlistComparer::do_device_assignment (const db::Circuit *c1, const db::NetGraph typedef std::vector >, std::pair > > unmatched_list; unmatched_list unmatched_a, unmatched_b; + // check mapping of devices whose equivalence is established topologically + for (db::Circuit::const_device_iterator d = c1->begin_devices (); d != c1->end_devices (); ++d) { if (! device_filter.filter (d.operator-> ())) { continue; } + if (device_eq.other (d.operator-> ())) { + continue; + } + size_t device_cat = device_categorizer.cat_for_device (d.operator-> ()); if (! device_cat) { // device is ignored continue; } - std::vector > k = compute_device_key (*d, g1, device_categorizer.is_strict_device_category (device_cat)); - bool mapped = true; - for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { - if (! g1.begin () [i->second].has_other ()) { - i->second = invalid_id; // normalization - mapped = false; - } - } + std::vector > k = compute_device_key_for_this (*d, g1, device_categorizer.is_strict_device_category (device_cat), mapped); if (! mapped) { if (mp_logger) { @@ -3296,53 +3889,84 @@ NetlistComparer::do_device_assignment (const db::Circuit *c1, const db::NetGraph continue; } - std::vector > k = compute_device_key (*d, g2, device_categorizer.is_strict_device_category (device_cat)); + const db::Device *c1_device = 0; + size_t c1_device_cat = 0; + + const db::Device *d_this = device_eq.other (d.operator-> ()); + if (d_this) { + + size_t device_cat_this = device_categorizer.cat_for_device (d_this); + if (! device_cat_this) { + // device is ignored + continue; + } + + bool mapped1 = true, mapped2 = true; + std::vector > k_this = compute_device_key_for_this (*d_this, g1, device_categorizer.is_strict_device_category (device_cat_this), mapped1); + std::vector > k = compute_device_key_for_other (*d, g2, device_categorizer.is_strict_device_category (device_cat), mapped2); + + if (! mapped1 || ! mapped2 || k != k_this) { + + // topological mismatch + if (mp_logger) { + mp_logger->device_mismatch (d_this, d.operator-> ()); + } + good = false; - bool mapped = true; - for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { - if (! g2.begin () [i->second].has_other ()) { - i->second = invalid_id; // normalization - mapped = false; } else { - i->second = g2.begin () [i->second].other_net_index (); + + c1_device = d_this; + c1_device_cat = device_cat_this; + } - } - - std::sort (k.begin (), k.end ()); - - std::multimap >, std::pair >::iterator dm = device_map.find (k); - - if (! mapped || dm == device_map.end () || dm->first != k) { - - if (mp_logger) { - unmatched_b.push_back (std::make_pair (k, std::make_pair (d.operator-> (), device_cat))); - } - good = false; } else { + bool mapped = true; + std::vector > k = compute_device_key_for_other (*d, g2, device_categorizer.is_strict_device_category (device_cat), mapped); + + std::multimap >, std::pair >::iterator dm = device_map.find (k); + + if (! mapped || dm == device_map.end () || dm->first != k) { + + if (mp_logger) { + unmatched_b.push_back (std::make_pair (k, std::make_pair (d.operator-> (), device_cat))); + } + good = false; + + } else { + + c1_device = dm->second.first; + c1_device_cat = dm->second.second; + + device_map.erase (dm); + + } + + } + + if (c1_device) { + db::DeviceCompare dc; - if (! dc.equals (dm->second, std::make_pair (d.operator-> (), device_cat))) { - if (dm->second.second != device_cat) { + if (! dc.equals (std::make_pair (c1_device, c1_device_cat), std::make_pair (d.operator-> (), device_cat))) { + if (c1_device_cat != device_cat) { if (mp_logger) { - mp_logger->match_devices_with_different_device_classes (dm->second.first, d.operator-> ()); + mp_logger->match_devices_with_different_device_classes (c1_device, d.operator-> ()); } good = false; } else { if (mp_logger) { - mp_logger->match_devices_with_different_parameters (dm->second.first, d.operator-> ()); + mp_logger->match_devices_with_different_parameters (c1_device, d.operator-> ()); } good = false; } } else { if (mp_logger) { - mp_logger->match_devices (dm->second.first, d.operator-> ()); + mp_logger->match_devices (c1_device, d.operator-> ()); } } - device_map.erase (dm); - } } @@ -3426,7 +4050,7 @@ NetlistComparer::do_device_assignment (const db::Circuit *c1, const db::NetGraph } void -NetlistComparer::do_subcircuit_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, CircuitCategorizer &circuit_categorizer, const CircuitPinMapper &circuit_pin_mapper, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping, bool &good) const +NetlistComparer::do_subcircuit_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, CircuitCategorizer &circuit_categorizer, const CircuitPinMapper &circuit_pin_mapper, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping, SubCircuitEquivalenceTracker &subcircuit_eq, bool &good) const { // Report subcircuit assignment @@ -3440,16 +4064,13 @@ NetlistComparer::do_subcircuit_assignment (const db::Circuit *c1, const db::NetG continue; } - std::vector > k; - bool valid = compute_subcircuit_key (*sc, g1, &c12_circuit_and_pin_mapping, &circuit_pin_mapper, k); - - bool mapped = true; - for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { - if (! g1.begin () [i->second].has_other ()) { - mapped = false; - } + if (subcircuit_eq.other (sc.operator-> ())) { + continue; } + bool mapped = true, valid = true; + std::vector > k = compute_subcircuit_key_for_this (*sc, g1, &c12_circuit_and_pin_mapping, &circuit_pin_mapper, mapped, valid); + if (! mapped) { if (mp_logger) { mp_logger->subcircuit_mismatch (sc.operator-> (), 0); @@ -3473,75 +4094,93 @@ NetlistComparer::do_subcircuit_assignment (const db::Circuit *c1, const db::NetG continue; } - std::vector > k; - compute_subcircuit_key (*sc, g2, &c22_circuit_and_pin_mapping, &circuit_pin_mapper, k); + const db::SubCircuit *sc_this = subcircuit_eq.other (sc.operator-> ()); + if (sc_this) { - bool mapped = true; - for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { - if (! g2.begin () [i->second].has_other ()) { - mapped = false; + size_t sc_cat_this = circuit_categorizer.cat_for_subcircuit (sc_this); + if (! sc_cat_this) { + // subcircuit is ignored + continue; + } + + bool mapped1 = true, mapped2 = true; + bool valid1 = true, valid2 = true; + std::vector > k_this = compute_subcircuit_key_for_this (*sc_this, g1, &c12_circuit_and_pin_mapping, &circuit_pin_mapper, mapped1, valid1); + std::vector > k = compute_subcircuit_key_for_other (*sc, g2, &c22_circuit_and_pin_mapping, &circuit_pin_mapper, mapped2, valid2); + + if (! valid1 || ! valid2 || ! mapped1 || ! mapped2 || k_this != k || sc_cat != sc_cat_this) { + if (mp_logger) { + mp_logger->subcircuit_mismatch (sc_this, sc.operator-> ()); + } + good = false; } else { - i->second = g2.begin () [i->second].other_net_index (); + if (mp_logger) { + mp_logger->match_subcircuits (sc_this, sc.operator-> ()); + } } - } - - std::sort (k.begin (), k.end ()); - - std::multimap >, std::pair >::iterator scm = subcircuit_map.find (k); - - if (! mapped || scm == subcircuit_map.end ()) { - - if (mp_logger) { - unmatched_b.push_back (std::make_pair (k, sc.operator-> ())); - } - good = false; } else { - db::SubCircuitCompare scc; + bool mapped = true, valid = true; + std::vector > k = compute_subcircuit_key_for_other (*sc, g2, &c22_circuit_and_pin_mapping, &circuit_pin_mapper, mapped, valid); - std::multimap >, std::pair >::iterator scm_start = scm; + std::multimap >, std::pair >::iterator scm = subcircuit_map.find (k); - bool found = false; - size_t nscm = 0; - while (! found && scm != subcircuit_map.end () && scm->first == k) { - ++nscm; - if (scc.equals (scm->second, std::make_pair (sc.operator-> (), sc_cat))) { - found = true; + if (! mapped || scm == subcircuit_map.end () || scm->first != k) { + + if (mp_logger) { + unmatched_b.push_back (std::make_pair (k, sc.operator-> ())); } - } - - if (! found) { - - if (nscm == 1) { - - // unique match, but doesn't fit: report this one as paired, but mismatching: - if (mp_logger) { - mp_logger->subcircuit_mismatch (scm_start->second.first, sc.operator-> ()); - } - - // no longer look for this one - subcircuit_map.erase (scm_start); - - } else { - - // no unqiue match - if (mp_logger) { - mp_logger->subcircuit_mismatch (0, sc.operator-> ()); - } - - } - good = false; } else { - if (mp_logger) { - mp_logger->match_subcircuits (scm->second.first, sc.operator-> ()); + db::SubCircuitCompare scc; + + std::multimap >, std::pair >::iterator scm_start = scm; + + bool found = false; + size_t nscm = 0; + while (! found && scm != subcircuit_map.end () && scm->first == k) { + ++nscm; + if (scc.equals (scm->second, std::make_pair (sc.operator-> (), sc_cat))) { + found = true; + } } - // no longer look for this one - subcircuit_map.erase (scm); + if (! found) { + + if (nscm == 1) { + + // unique match, but doesn't fit: report this one as paired, but mismatching: + if (mp_logger) { + mp_logger->subcircuit_mismatch (scm_start->second.first, sc.operator-> ()); + } + + // no longer look for this one + subcircuit_map.erase (scm_start); + + } else { + + // no unqiue match + if (mp_logger) { + mp_logger->subcircuit_mismatch (0, sc.operator-> ()); + } + + } + + good = false; + + } else { + + if (mp_logger) { + mp_logger->match_subcircuits (scm->second.first, sc.operator-> ()); + } + + // no longer look for this one + subcircuit_map.erase (scm); + + } } @@ -3731,12 +4370,15 @@ NetlistComparer::join_symmetric_nets (db::Circuit *circuit) // nodes are identical if the attached devices and circuits are of the same kind and with the same parameters // and connect to other nodes in identical configurations. - std::vector nodes; + std::vector > nodes; + + std::vector no_edges; + no_edges.push_back (NetGraphNode::edge_type ()); 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-> ()); + nodes.push_back (std::make_pair (i.operator-> (), no_edges.begin ())); } } @@ -3746,18 +4388,18 @@ NetlistComparer::join_symmetric_nets (db::Circuit *circuit) tl::equivalence_clusters identical_nodes; - for (std::vector::const_iterator np = nodes.begin (); np + 1 != nodes.end (); ++np) { - if (*np[0] == *np[1]) { - identical_nodes.same (np[0], np[1]); + for (std::vector >::const_iterator np = nodes.begin (); np + 1 != nodes.end (); ++np) { + if (*np[0].first == *np[1].first) { + identical_nodes.same (np[0].first, np[1].first); } } std::vector > symmetry_groups; std::set visited; - for (std::vector::const_iterator np = nodes.begin (); np != nodes.end (); ++np) { + for (std::vector >::const_iterator np = nodes.begin (); np != nodes.end (); ++np) { - size_t node_id = graph.node_index_for_net (np[0]->net ()); + size_t node_id = graph.node_index_for_net (np[0].first->net ()); if (visited.find (node_id) != visited.end ()) { continue; } diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index d639ed12f..2edd6062c 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -38,6 +38,8 @@ class DeviceCategorizer; class CircuitCategorizer; class CircuitMapper; class NetGraph; +class SubCircuitEquivalenceTracker; +class DeviceEquivalenceTracker; /** * @brief A receiver for netlist compare events @@ -285,6 +287,26 @@ public: return m_max_n_branch; } + /** + * @brief Sets a value indicating depth-first traversal + * + * With depth first (the default), the algorithm looks for further identities before moving to another + * node. With breadth first (false), the algorithm will work in "waves" rather than digging deerly + * into the direction of a node. + */ + void set_depth_first (bool df) + { + m_depth_first = df; + } + + /** + * @brief Gets a value indicating depth-first traversal + */ + bool depth_first () const + { + return m_depth_first; + } + /** * @brief Gets the list of circuits without matching circuit in the other netlist * The result can be used to flatten these circuits prior to compare. @@ -326,8 +348,8 @@ protected: bool all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const; static void derive_pin_equivalence (const db::Circuit *ca, const db::Circuit *cb, CircuitPinMapper *circuit_pin_mapper); void do_pin_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping, bool &pin_mismatch, bool &good) const; - void do_device_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, const db::DeviceFilter &device_filter, DeviceCategorizer &device_categorizer, bool &good) const; - void do_subcircuit_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, CircuitCategorizer &circuit_categorizer, const db::CircuitPinMapper &circuit_pin_mapper, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping, bool &good) const; + void do_device_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, const db::DeviceFilter &device_filter, DeviceCategorizer &device_categorizer, db::DeviceEquivalenceTracker &device_eq, bool &good) const; + void do_subcircuit_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, CircuitCategorizer &circuit_categorizer, const db::CircuitPinMapper &circuit_pin_mapper, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping, db::SubCircuitEquivalenceTracker &subcircuit_eq, bool &good) const; bool handle_pin_mismatch (const NetGraph &g1, const db::Circuit *c1, const db::Pin *pin1, const NetGraph &g2, const db::Circuit *c2, const db::Pin *p2) const; mutable NetlistCompareLogger *mp_logger; @@ -339,6 +361,7 @@ protected: double m_res_threshold; size_t m_max_n_branch; size_t m_max_depth; + bool m_depth_first; bool m_dont_consider_net_names; }; diff --git a/src/db/db/dbSaveLayoutOptions.cc b/src/db/db/dbSaveLayoutOptions.cc index 77c7663a3..355ed11c8 100644 --- a/src/db/db/dbSaveLayoutOptions.cc +++ b/src/db/db/dbSaveLayoutOptions.cc @@ -26,6 +26,7 @@ #include "tlClassRegistry.h" #include "tlStream.h" #include "tlExpression.h" +#include "tlInternational.h" namespace db { @@ -327,7 +328,7 @@ SaveLayoutOptions::get_valid_layers (const db::Layout &layout, std::vector &cells, const std::vector > &valid_layers) const +SaveLayoutOptions::get_cells (const db::Layout &layout, std::set &cells, const std::vector > &valid_layers, bool require_unique_names) const { if (m_all_cells) { @@ -408,6 +409,26 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set use_count; + for (std::set ::const_iterator c = cells.begin (); c != cells.end (); ++c) { + use_count.insert (std::make_pair (std::string (layout.cell_name (*c)), 0)).first->second += 1; + } + + std::vector multi; + for (std::map::const_iterator u = use_count.begin (); u != use_count.end (); ++u) { + if (u->second > 1) { + multi.push_back (u->first); + } + } + + if (! multi.empty ()) { + throw tl::Exception (tl::to_string (tr ("The following cell name(s) are used for more than one cell - can't write this layout:\n ")) + tl::join (multi, "\n ")); + } + + } } bool diff --git a/src/db/db/dbSaveLayoutOptions.h b/src/db/db/dbSaveLayoutOptions.h index 5dd0a1a80..7c6da0c74 100644 --- a/src/db/db/dbSaveLayoutOptions.h +++ b/src/db/db/dbSaveLayoutOptions.h @@ -413,7 +413,7 @@ public: * * It must be given a list of valid layers which is used to determine empty cells if dont_save_empty_cells is true. */ - void get_cells (const db::Layout &layout, std::set &cells, const std::vector > &valid_layers) const; + void get_cells (const db::Layout &layout, std::set &cells, const std::vector > &valid_layers, bool require_unique_names = true) const; /** * @brief Sets a layout reader option by name diff --git a/src/db/db/dbShapeIterator.cc b/src/db/db/dbShapeIterator.cc index a0e9db1e1..f4433255b 100644 --- a/src/db/db/dbShapeIterator.cc +++ b/src/db/db/dbShapeIterator.cc @@ -546,9 +546,13 @@ ShapeIterator::advance_aref (int &mode) if (mode && m_array_iterator_valid) { - if (mode > 0) { + if (mode == 1) { array_iterator *arr_iter = (array_iterator *) m_ad.iter; ++*arr_iter; + } else if (mode == 2) { + // skip array quad -> skip rest of array quad and move to shape in the next quad or to end + do_skip_array_quad (); + mode = 1; } else { // skip quad -> skip rest of array and move to next shape array skip_array (); // sets m_array_iterator_valid = false @@ -810,9 +814,100 @@ ShapeIterator::quad_box () const return quad_box_generic (); } } + return db::Box (); } +template +void +ShapeIterator::do_skip_array_quad_iter () +{ + Iter *arr_iter = (Iter *) m_ad.iter; + arr_iter->skip_quad (); +} + +void +ShapeIterator::do_skip_array_quad () +{ + if (m_array_iterator_valid) { + if (m_type == PolygonPtrArray) { + do_skip_array_quad_iter (); + } else if (m_type == SimplePolygonPtrArray) { + do_skip_array_quad_iter (); + } else if (m_type == PathPtrArray) { + do_skip_array_quad_iter (); + } else if (m_type == TextPtrArray) { + do_skip_array_quad_iter (); + } else if (m_type == BoxArray) { + do_skip_array_quad_iter (); + } else if (m_type == ShortBoxArray) { + do_skip_array_quad_iter (); + } + } +} + +template +size_t +ShapeIterator::get_array_quad_id () const +{ + Iter *arr_iter = (Iter *) m_ad.iter; + return arr_iter->quad_id (); +} + +size_t +ShapeIterator::array_quad_id () const +{ + if (m_array_iterator_valid) { + if (m_type == PolygonPtrArray) { + return get_array_quad_id (); + } else if (m_type == SimplePolygonPtrArray) { + return get_array_quad_id (); + } else if (m_type == PathPtrArray) { + return get_array_quad_id (); + } else if (m_type == TextPtrArray) { + return get_array_quad_id (); + } else if (m_type == BoxArray) { + return get_array_quad_id (); + } else if (m_type == ShortBoxArray) { + return get_array_quad_id (); + } + } + + return 0; +} + +template +db::Box +ShapeIterator::get_array_quad_box () const +{ + const Array *arr = m_array.basic_ptr (typename Array::tag ()); + Iter *arr_iter = (Iter *) m_ad.iter; + db::box_convert bc; + return arr->quad_box (*arr_iter, bc); +} + +db::Box +ShapeIterator::array_quad_box () const +{ + if (m_array_iterator_valid) { + if (m_type == PolygonPtrArray) { + return get_array_quad_box (); + } else if (m_type == SimplePolygonPtrArray) { + return get_array_quad_box (); + } else if (m_type == PathPtrArray) { + return get_array_quad_box (); + } else if (m_type == TextPtrArray) { + return get_array_quad_box (); + } else if (m_type == BoxArray) { + return get_array_quad_box (); + } else if (m_type == ShortBoxArray) { + return get_array_quad_box (); + } + } + + return db::Box::world (); +} + void ShapeIterator::cleanup () { diff --git a/src/db/db/dbShapes.h b/src/db/db/dbShapes.h index ce8368518..1942906a0 100644 --- a/src/db/db/dbShapes.h +++ b/src/db/db/dbShapes.h @@ -319,6 +319,33 @@ public: advance (-1); } + /** + * @brief Gets the arrays quad ID + * + * The arrays quad ID is a unique identifier for the current quad for iterated arrays. This can be used to + * detect whether the iterator entered a new quad and optimize the search in that case. + */ + size_t array_quad_id () const; + + /** + * @brief Gets the quad box + * + * Gets the box the current quad uses. This box may be larger than the actual shape containers + * bounding box. Specifically if there is no quad tree at all, this box is the world box. + */ + db::Box array_quad_box () const; + + /** + * @brief Skips the current quad + * + * Moves to the next quad. This method can be used to shortcut searching if we are inside + * a quad that is not relevant for the search. + */ + void skip_array_quad () + { + advance (2); + } + private: // a helper union for the iter_size union // (basically computing the size required for all iterators for a certain shape/array type) @@ -418,6 +445,12 @@ private: template db::Box quad_box_by_shape (OverlappingRegionTag) const; template db::Box quad_box_generic () const; + template db::Box get_array_quad_box () const; + template size_t get_array_quad_id () const; + + template void do_skip_array_quad_iter (); + void do_skip_array_quad (); + template bool advance_shape (int &mode); template void init_array_iter (NoRegionTag); template void init_array_iter (TouchingRegionTag); diff --git a/src/db/db/dbStreamLayers.cc b/src/db/db/dbStreamLayers.cc index 37c4dfcce..aa916b30e 100644 --- a/src/db/db/dbStreamLayers.cc +++ b/src/db/db/dbStreamLayers.cc @@ -305,7 +305,7 @@ LayerMap::prepare (db::Layout &layout) // In addition, map other existing layers as well, so merging of layout is somewhat better supported for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { - if (mapped_layers.find ((*l).first) == mapped_layers.end ()) { + if (! (*l).second->is_null () && mapped_layers.find ((*l).first) == mapped_layers.end ()) { map (*(*l).second, (*l).first); } } diff --git a/src/db/db/dbTextWriter.cc b/src/db/db/dbTextWriter.cc index 2dc06d0d3..d484a7a11 100644 --- a/src/db/db/dbTextWriter.cc +++ b/src/db/db/dbTextWriter.cc @@ -198,43 +198,52 @@ TextWriter::write (const db::Layout &layout) for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { - std::string pfx = ""; - - if (inst->has_prop_id () && inst->prop_id () != 0) { - pfx = "p $props"; - write_props (layout, inst->prop_id ()); - } - db::Vector a, b; unsigned long amax, bmax; bool is_reg = inst->is_regular_array (a, b, amax, bmax); - *this << (is_reg ? "aref" : "sref") << pfx << " {" << layout.cell_name (inst->cell_index ()) << "}"; + for (db::CellInstArray::iterator i = inst->begin (); ! i.at_end (); ++i) { - db::Trans t = inst->front (); + std::string pfx = ""; + + if (inst->has_prop_id () && inst->prop_id () != 0) { + pfx = "p $props"; + write_props (layout, inst->prop_id ()); + } + + *this << (is_reg ? "aref" : "sref") << pfx << " {" << layout.cell_name (inst->cell_index ()) << "}"; + + db::Trans t = *i; + + if (inst->is_complex ()) { + db::CellInstArray::complex_trans_type ct = inst->complex_trans (t); + *this << " " << ct.angle (); + *this << " " << (ct.is_mirror () ? 1 : 0); + *this << " " << ct.mag (); + } else { + *this << " " << (t.rot () % 4) * 90.0; + *this << " " << (t.is_mirror () ? 1 : 0); + *this << " " << 1.0; + } + + if (is_reg) { + *this << " " << int (std::max ((unsigned long) 1, amax)); + *this << " " << int (std::max ((unsigned long) 1, bmax)); + } + *this << " " << t.disp (); + if (is_reg) { + *this << " " << (t.disp () + a * (long) amax); + *this << " " << (t.disp () + b * (long) bmax); + } + *this << endl (); + + if (is_reg) { + break; + } - if (inst->is_complex ()) { - *this << " " << inst->complex_trans ().angle (); - *this << " " << (inst->complex_trans ().is_mirror () ? 1 : 0); - *this << " " << inst->complex_trans ().mag (); - } else { - *this << " " << (t.rot () % 4) * 90.0; - *this << " " << (t.is_mirror () ? 1 : 0); - *this << " " << 1.0; } - if (is_reg) { - *this << " " << int (std::max ((unsigned long) 1, amax)); - *this << " " << int (std::max ((unsigned long) 1, bmax)); - } - *this << " " << t.disp (); - if (is_reg) { - *this << " " << (t.disp () + a * (long) amax); - *this << " " << (t.disp () + b * (long) bmax); - } - *this << endl (); - } end_sorted_section (); diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index bf4000a24..e41ef5972 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -251,6 +251,8 @@ struct cell_inst_array_defs unsigned long na = 1, nb = 1; if (arr->is_regular_array (a, b, na, nb)) { *arr = C (arr->object (), t, a, b, na, nb); + } else if (arr->is_iterated_array ()) { + throw tl::Exception (tl::to_string (tr ("Can't set the transformation on an iterated array (layout not editable?)"))); } else { *arr = C (arr->object (), t); } @@ -262,6 +264,8 @@ struct cell_inst_array_defs unsigned long na = 1, nb = 1; if (arr->is_regular_array (a, b, na, nb)) { *arr = C (arr->object (), t, a, b, na, nb); + } else if (arr->is_iterated_array ()) { + throw tl::Exception (tl::to_string (tr ("Can't set the transformation on an iterated array (layout not editable?)"))); } else { *arr = C (arr->object (), t); } @@ -292,6 +296,8 @@ struct cell_inst_array_defs s += "*"; s += tl::to_string (nb); s += "]"; + } else if (arr->size () > 1) { + s += std::string (" (+") + tl::to_string (arr->size () - 1) + " irregular locations)"; } return s; @@ -303,14 +309,27 @@ struct cell_inst_array_defs { typedef db::array > target_array; + std::vector iterated; + std::vector iterated_transformed; typename C::vector_type a, b; unsigned long amax = 0, bmax = 0; + if (arr.is_regular_array (a, b, amax, bmax)) { if (arr.is_complex ()) { return target_array (arr.object (), t * arr.complex_trans () * t.inverted (), t * a, t * b, amax, bmax); } else { return target_array (arr.object (), typename target_array::trans_type (t * typename C::complex_trans_type (arr.front ()) * t.inverted ()), t * a, t * b, amax, bmax); } + } else if (arr.is_iterated_array (&iterated)) { + iterated_transformed.reserve (iterated.size ()); + for (typename std::vector::const_iterator i = iterated.begin (); i != iterated.end (); ++i) { + iterated_transformed.push_back (t * *i); + } + if (arr.is_complex ()) { + return target_array (arr.object (), t * arr.complex_trans () * t.inverted (), iterated_transformed.begin (), iterated_transformed.end ()); + } else { + return target_array (arr.object (), typename target_array::trans_type (t * typename C::complex_trans_type (arr.front ()) * t.inverted ()), iterated_transformed.begin (), iterated_transformed.end ()); + } } else if (arr.is_complex ()) { return target_array (arr.object (), t * arr.complex_trans () * t.inverted ()); } else { @@ -352,93 +371,17 @@ struct cell_inst_array_defs static bool less (const C *i, const C &other) { - if (i->object ().cell_index () != other.object ().cell_index ()) { - return i->object ().cell_index () < other.object ().cell_index (); - } - - db::vector a, b; - unsigned long na = 1, nb = 1; - bool r = i->is_regular_array (a, b, na, nb); - - db::vector aother, bother; - unsigned long naother = 1, nbother = 1; - bool rother = other.is_regular_array (aother, bother, naother, nbother); - - if (r != rother) { - return r < rother; - } else if (r) { - if (a.not_equal (aother)) { - return a.less (aother); - } - if (b.not_equal (bother)) { - return b.less (bother); - } - if (na != naother) { - return na < naother; - } - if (nb != nbother) { - return nb < nbother; - } - } - - bool c = i->is_complex (); - bool cother = other.is_complex (); - - if (c != cother) { - return c < cother; - } else if (c) { - return i->complex_trans ().less (other.complex_trans ()); - } else { - return i->front ().less (other.front ()); - } + return i->less (other); } static bool equal (const C *i, const C &other) { - if (i->object ().cell_index () != other.object ().cell_index ()) { - return false; - } - - db::vector a, b; - unsigned long na = 1, nb = 1; - bool r = i->is_regular_array (a, b, na, nb); - - db::vector aother, bother; - unsigned long naother = 1, nbother = 1; - bool rother = other.is_regular_array (aother, bother, naother, nbother); - - if (r != rother) { - return false; - } else if (r) { - if (a.not_equal (aother)) { - return false; - } - if (b.not_equal (bother)) { - return false; - } - if (na != naother) { - return false; - } - if (nb != nbother) { - return false; - } - } - - bool c = i->is_complex (); - bool cother = other.is_complex (); - - if (c != cother) { - return false; - } else if (c) { - return i->complex_trans ().equal (other.complex_trans ()); - } else { - return i->front ().equal (other.front ()); - } + return i->equal (other); } static bool not_equal (const C *i, const C &other) { - return ! equal (i, other); + return ! i->equal (other); } static gsi::Methods methods (bool new_doc) @@ -508,7 +451,9 @@ struct cell_inst_array_defs ) + gsi::method ("size", &C::size, "@brief Gets the number of single instances in the array\n" - "If the instance represents a single instance, the count is 1. Otherwise it is na*nb." + "If the instance represents a single instance, the count is 1. Otherwise it is na*nb. " + "Starting with version 0.27, there may be iterated instances for which the size is larger than 1, but \\is_regular_array? will return false. " + "In this case, use \\each_trans or \\each_cplx_trans to retrieve the individual placements of the iterated instance." ) + gsi::method_ext ("cell_index", &cell_index, "@brief Gets the cell index of the cell instantiated \n" @@ -1618,6 +1563,28 @@ static db::Instance cell_inst_dtransform_into_cplx (db::Cell *cell, const db::In return cell->transform_into (inst, dbu_trans.inverted () * t * dbu_trans); } +static void cell_dtransform_simple (db::Cell *cell, const db::DTrans &t) +{ + const db::Layout *layout = cell->layout (); + if (! layout) { + throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer-unit transformation"))); + } + + db::CplxTrans dbu_trans (layout->dbu ()); + cell->transform (db::Trans (dbu_trans.inverted () * db::DCplxTrans (t) * dbu_trans)); +} + +static void cell_dtransform_cplx (db::Cell *cell, const db::DCplxTrans &t) +{ + const db::Layout *layout = cell->layout (); + if (! layout) { + throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer-unit transformation"))); + } + + db::CplxTrans dbu_trans (layout->dbu ()); + cell->transform (dbu_trans.inverted () * t * dbu_trans); +} + static void cell_dtransform_into_simple (db::Cell *cell, const db::DTrans &t) { const db::Layout *layout = cell->layout (); @@ -2402,6 +2369,46 @@ Class decl_Cell ("db", "Cell", "\n" "This variant has been introduced in version 0.25." ) + + gsi::method ("transform", (void (db::Cell::*)(const db::Trans &)) &db::Cell::transform, gsi::arg ("trans"), + "@brief Transforms the cell by the given integer transformation\n" + "\n" + "This method transforms all instances and all shapes by the given transformation. " + "There is a variant called \\transform_into which applies the transformation to instances " + "in a way such that it can be applied recursively to the child cells.\n" + "\n" + "This method has been introduced in version 0.26.7." + ) + + gsi::method ("transform", (void (db::Cell::*)(const db::ICplxTrans &)) &db::Cell::transform, gsi::arg ("trans"), + "@brief Transforms the cell by the given complex integer transformation\n" + "\n" + "This method transforms all instances and all shapes by the given transformation. " + "There is a variant called \\transform_into which applies the transformation to instances " + "in a way such that it can be applied recursively to the child cells. The difference is important in " + "the presence of magnifications: \"transform\" will leave magnified instances while \"transform_into\" " + "will not do so but expect the magnification to be applied inside the called cells too.\n" + "\n" + "This method has been introduced in version 0.26.7." + ) + + gsi::method_ext ("transform", &cell_dtransform_simple, gsi::arg ("trans"), + "@brief Transforms the cell by the given, micrometer-unit transformation\n" + "\n" + "This method transforms all instances and all shapes by the given transformation. " + "There is a variant called \\transform_into which applies the transformation to instances " + "in a way such that it can be applied recursively to the child cells.\n" + "\n" + "This method has been introduced in version 0.26.7." + ) + + gsi::method_ext ("transform", &cell_dtransform_cplx, gsi::arg ("trans"), + "@brief Transforms the cell by the given, micrometer-unit transformation\n" + "\n" + "This method transforms all instances and all shapes by the given transformation. " + "There is a variant called \\transform_into which applies the transformation to instances " + "in a way such that it can be applied recursively to the child cells. The difference is important in " + "the presence of magnifications: \"transform\" will leave magnified instances while \"transform_into\" " + "will not do so but expect the magnification to be applied inside the called cells too.\n" + "\n" + "This method has been introduced in version 0.26.7." + ) + gsi::method_ext ("transform_into", &cell_dtransform_into_simple, gsi::arg ("trans"), "@brief Transforms the cell into a new coordinate system with the given transformation where the transformation is in micrometer units\n" "This method is identical to the corresponding \\transform_into method with a \\Trans argument. For this variant " diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index 98f737d88..88f205373 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -78,7 +78,18 @@ static std::string get_technology (db::Library *lib) } } +static void dummy_destroy (db::Library *) { } + Class decl_Library ("db", "Library", + gsi::method_ext ("_destroy", &dummy_destroy, + "@brief An inactive substitute for _destroy (delete the object)\n" + "As libraries need to be kept if cells are using them, library objects must " + "not be deleted. Hence the default '_destroy' implementation must not be called. " + "To keep old code working, this substitute is provided. It just returns without " + "deleting the object.\n" + "\n" + "This method has been introduced in version 0.26.7." + ) + gsi::constructor ("new", &new_lib, "@brief Creates a new, empty library" ) + diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index df91db260..46c1b8725 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -538,6 +538,18 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "@brief Gets the maximum branch complexity\n" "See \\max_branch_complexity= for details." ) + + gsi::method ("dont_consider_net_names=", &db::NetlistComparer::set_dont_consider_net_names, gsi::arg ("f"), + "@brief Sets a value indicating whether net names shall not be considered\n" + "If this value is set to true, net names will not be considered when resolving ambiguities.\n" + "Not considering net names usually is more expensive. The default is 'false' indicating that\n" + "net names will be considered for ambiguity resolution.\n" + "\n" + "This property has been introduced in version 0.26.7.\n" + ) + + gsi::method ("dont_consider_net_names", &db::NetlistComparer::dont_consider_net_names, + "@brief Gets a value indicating whether net names shall not be considered\n" + "See \\dont_consider_net_names= for details." + ) + gsi::method_ext ("unmatched_circuits_a", &unmatched_circuits_a, gsi::arg ("a"), gsi::arg ("b"), "@brief Returns a list of circuits in A for which there is not corresponding circuit in B\n" "This list can be used to flatten these circuits so they do not participate in the compare process.\n" diff --git a/src/db/unit_tests/dbBoxTreeTests.cc b/src/db/unit_tests/dbBoxTreeTests.cc index c2cbe01ed..9b7e3feb5 100644 --- a/src/db/unit_tests/dbBoxTreeTests.cc +++ b/src/db/unit_tests/dbBoxTreeTests.cc @@ -1031,4 +1031,92 @@ TEST(6U) } +TEST(7) +{ + Box2Box conv; + TestTree t; + int n = 200000; + + for (int i = n - 1; i >= 0; --i) { + t.insert (db::Box (i * 10, 0, i * 10 + 5, 5)); + } + t.sort (conv); + + { + tl::SelfTimer timer ("test 7 lookup"); + size_t n = 0; + for (unsigned int i = 0; i < 2000; ++i) { + db::Coord sx = 0, sy = 0; + TestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (2000, 0), db::Point (3000, 0)), conv); + while (!it.at_end ()) { + sx += abs (it->left ()); + sy += abs (it->bottom ()); + ++it; + ++n; + } + EXPECT_EQ (sx, 252500); + EXPECT_EQ (sy, 0); + } + EXPECT_EQ (n, size_t (101 * 2000)); + } + + { + tl::SelfTimer timer ("test 7 traverse"); + db::Coord m = std::numeric_limits::max (); + size_t n = 0; + for (unsigned int i = 0; i < 10; ++i) { + TestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (-m,-m), db::Point (m, m)), conv); + while (!it.at_end ()) { + ++it; + ++n; + } + } + EXPECT_EQ (n, t.size () * 10); + } +} + +TEST(7U) +{ + Box2Box conv; + UnstableTestTree t; + + int n = 200000; + + for (int i = n - 1; i >= 0; --i) { + t.insert (db::Box (i * 10, 0, i * 10 + 5, 5)); + } + t.sort (conv); + + { + tl::SelfTimer timer ("test 7U lookup"); + size_t n = 0; + for (unsigned int i = 0; i < 2000; ++i) { + db::Coord sx = 0, sy = 0; + UnstableTestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (2000, 0), db::Point (3000, 0)), conv); + while (!it.at_end ()) { + sx += abs (it->left ()); + sy += abs (it->bottom ()); + ++it; + ++n; + } + EXPECT_EQ (sx, 252500); + EXPECT_EQ (sy, 0); + } + EXPECT_EQ (n, size_t (101 * 2000)); + } + + { + tl::SelfTimer timer ("test 7U traverse"); + db::Coord m = std::numeric_limits::max (); + size_t n = 0; + for (unsigned int i = 0; i < 10; ++i) { + UnstableTestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (-m,-m), db::Point (m, m)), conv); + while (!it.at_end ()) { + ++it; + ++n; + } + } + EXPECT_EQ (n, t.size () * 10); + } +} diff --git a/src/db/unit_tests/dbCellTests.cc b/src/db/unit_tests/dbCellTests.cc index 7e3dfec0e..86633b7c7 100644 --- a/src/db/unit_tests/dbCellTests.cc +++ b/src/db/unit_tests/dbCellTests.cc @@ -722,6 +722,14 @@ TEST(3a) c0.transform_into (db::ICplxTrans (ti)); inst = *c0.begin (); EXPECT_EQ (inst.to_string (), "cell_index=1 m90 -334,0"); + + c0.transform (db::Trans (5)); + inst = *c0.begin (); + EXPECT_EQ (inst.to_string (), "cell_index=1 r270 0,-334"); + + c0.transform (db::ICplxTrans (ti)); + inst = *c0.begin (); + EXPECT_EQ (inst.to_string (), "cell_index=1 r315 *2.5 600,-570"); } TEST(3b) @@ -791,6 +799,73 @@ TEST(3b) } } +TEST(3c) +{ + ::pi = 0; + + db::Manager m (true); + db::Layout g (&m); + db::Cell &c0 (g.cell (g.add_cell ())); + db::Cell &c1 (g.cell (g.add_cell ())); + + db::Trans t (db::Vector (100, -100)); + c0.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (c1.cell_index ()), t), 5)); + + db::Box b (0, 100, 1000, 1200); + c0.shapes (0).insert (db::BoxWithProperties (b, 17)); + c1.shapes (1).insert (b); + + // Note: this requires editable mode since db::Shapes::erase is permitted in editable mode only + // (erase is triggered by undo) + if (db::default_editable_mode ()) { + + m.transaction ("t"); + c0.transform (db::ICplxTrans (2.5)); + m.commit (); + + EXPECT_EQ (c1.cell_instances (), size_t (0)); + EXPECT_EQ (c0.cell_instances (), size_t (1)); + EXPECT_EQ (c0.begin ()->to_string (), "cell_index=1 r0 *2.5 250,-250 prop_id=5"); + + EXPECT_EQ (c0.shapes (0).size (), size_t (1)); + EXPECT_EQ (c0.shapes (1).size (), size_t (0)); + EXPECT_EQ (c1.shapes (0).size (), size_t (0)); + EXPECT_EQ (c1.shapes (1).size (), size_t (1)); + + EXPECT_EQ (c0.shapes (0).begin (db::ShapeIterator::All)->to_string (), "box (0,250;2500,3000) prop_id=17"); + EXPECT_EQ (c1.shapes (1).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200)"); + + m.undo (); + + EXPECT_EQ (c1.cell_instances (), size_t (0)); + EXPECT_EQ (c0.cell_instances (), size_t (1)); + EXPECT_EQ (c0.begin ()->to_string (), "cell_index=1 r0 100,-100 prop_id=5"); + + EXPECT_EQ (c0.shapes (0).size (), size_t (1)); + EXPECT_EQ (c0.shapes (1).size (), size_t (0)); + EXPECT_EQ (c1.shapes (0).size (), size_t (0)); + EXPECT_EQ (c1.shapes (1).size (), size_t (1)); + + EXPECT_EQ (c0.shapes (0).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200) prop_id=17"); + EXPECT_EQ (c1.shapes (1).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200)"); + + m.redo (); + + EXPECT_EQ (c1.cell_instances (), size_t (0)); + EXPECT_EQ (c0.cell_instances (), size_t (1)); + EXPECT_EQ (c0.begin ()->to_string (), "cell_index=1 r0 *2.5 250,-250 prop_id=5"); + + EXPECT_EQ (c0.shapes (0).size (), size_t (1)); + EXPECT_EQ (c0.shapes (1).size (), size_t (0)); + EXPECT_EQ (c1.shapes (0).size (), size_t (0)); + EXPECT_EQ (c1.shapes (1).size (), size_t (1)); + + EXPECT_EQ (c0.shapes (0).begin (db::ShapeIterator::All)->to_string (), "box (0,250;2500,3000) prop_id=17"); + EXPECT_EQ (c1.shapes (1).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200)"); + + } +} + struct map1 { db::cell_index_type operator() (db::cell_index_type i) const { return 3-i; } diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 344101665..99539f854 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -1287,3 +1287,69 @@ TEST(120_HierClustersCombArrays) run_hc_test (_this, "comb2.gds", "comb2_au1.gds"); run_hc_test_with_backannotation (_this, "comb2.gds", "comb2_au2.gds"); } + +static size_t root_nets (const db::connected_clusters &cc) +{ + size_t n = 0; + for (db::connected_clusters::all_iterator c = cc.begin_all (); !c.at_end (); ++c) { + if (cc.is_root (*c)) { + ++n; + } + } + return n; +} + +// issue #609 +TEST(200_issue609) +{ + db::Layout ly; + unsigned int l1 = 0, l2 = 0; + + { + db::LayerProperties p; + db::LayerMap lmap; + + p.layer = 1; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l1 = ly.insert_layer ()); + ly.set_properties (l1, p); + + p.layer = 2; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), l2 = ly.insert_layer ()); + ly.set_properties (l2, p); + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/issue-609.oas.gz"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + std::vector strings; + normalize_layer (ly, strings, l1); + normalize_layer (ly, strings, l2); + + // connect 1 to 1, 1 to 2 + db::Connectivity conn; + conn.connect (l1, l1); + conn.connect (l2, l2); + conn.connect (l1, l2); + + db::hier_clusters hc; + hc.build (ly, ly.cell (*ly.begin_top_down ()), conn); + + db::Layout::top_down_const_iterator td = ly.begin_top_down (); + EXPECT_EQ (td != ly.end_top_down (), true); + EXPECT_EQ (root_nets (hc.clusters_per_cell (*td)), size_t (1)); + ++td; + + // result needs to be a single net + for ( ; td != ly.end_top_down (); ++td) { + EXPECT_EQ (root_nets (hc.clusters_per_cell (*td)), size_t (0)); + } +} diff --git a/src/db/unit_tests/dbLayoutDiffTests.cc b/src/db/unit_tests/dbLayoutDiffTests.cc index e4b533a2b..5dadccfd2 100644 --- a/src/db/unit_tests/dbLayoutDiffTests.cc +++ b/src/db/unit_tests/dbLayoutDiffTests.cc @@ -97,6 +97,8 @@ TestDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperties & unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { m_os << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]"; + } else if (ci.size () > 1) { + m_os << " (+" << (ci.size () - 1) << " irregular placements)"; } if (ci.properties_id () != 0) { m_os << " [" << ci.properties_id () << "]" << std::endl; @@ -112,6 +114,8 @@ TestDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperties & unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { m_os << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]"; + } else if (ci.size () > 1) { + m_os << " (+" << (ci.size () - 1) << " irregular placements)"; } if (ci.properties_id () != 0) { m_os << " [" << ci.properties_id () << "]" << std::endl; diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index c0d190f6d..b98d25af7 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -449,6 +449,7 @@ TEST(1_SimpleInverter) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -470,6 +471,7 @@ TEST(1_SimpleInverter) db::NetlistCrossReference xref; db::NetlistComparer comp_xref (&xref); + comp.set_dont_consider_net_names (true); good = comp_xref.compare (&nl1, &nl2); @@ -519,6 +521,7 @@ TEST(1_SimpleInverterMatchedDeviceClasses) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); comp.same_device_classes (nl1.device_class_by_name ("PMOS"), nl2.device_class_by_name ("PMOSB")); bool good = comp.compare (&nl1, &nl2); @@ -571,6 +574,7 @@ TEST(1_SimpleInverterSkippedDevices) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -594,6 +598,7 @@ TEST(1_SimpleInverterSkippedDevices) db::NetlistCrossReference xref; db::NetlistComparer comp_xref (&xref); + comp.set_dont_consider_net_names (true); good = comp_xref.compare (&nl1, &nl2); @@ -751,6 +756,8 @@ TEST(2_SimpleInverterWithForcedNetAssignment) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); + const db::Circuit *ca = nl1.circuit_by_name ("INV"); const db::Circuit *cb = nl2.circuit_by_name ("INV"); comp.same_nets (ca->net_by_name ("VDD"), cb->net_by_name ("VDD")); @@ -797,6 +804,7 @@ TEST(3_Buffer) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -852,6 +860,7 @@ TEST(4_BufferTwoPaths) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -912,6 +921,7 @@ TEST(5_BufferTwoPathsDifferentParameters) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); // Forcing the power nets into equality makes the parameter error harder to detect const db::Circuit *ca = nl1.circuit_by_name ("BUF"); @@ -1134,6 +1144,7 @@ TEST(5_BufferTwoPathsDifferentDeviceClasses) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); // NOTE: adding this power hint makes the device class error harder to detect const db::Circuit *ca = nl1.circuit_by_name ("BUF"); @@ -1147,8 +1158,8 @@ TEST(5_BufferTwoPathsDifferentDeviceClasses) "begin_circuit BUF BUF\n" "match_nets INT $10\n" "match_nets IN IN\n" - "net_mismatch OUT OUT\n" "net_mismatch INT2 $11\n" + "net_mismatch OUT OUT\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -1199,6 +1210,7 @@ TEST(6_BufferTwoPathsAdditionalResistor) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); // Forcing the power nets into equality makes the resistor error harder to detect const db::Circuit *ca = nl1.circuit_by_name ("BUF"); @@ -1212,8 +1224,35 @@ TEST(6_BufferTwoPathsAdditionalResistor) "begin_circuit BUF BUF\n" "net_mismatch INT $10\n" "match_nets IN IN\n" + "net_mismatch INT2 $11\n" + "match_nets OUT OUT\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $5 $3\n" + "match_devices $7 $4\n" + "match_devices $2 $5\n" + "match_devices $4 $6\n" + "match_devices $6 $7\n" + "match_devices $8 $8\n" + "device_mismatch (null) $9\n" + "end_circuit BUF BUF NOMATCH" + ); + EXPECT_EQ (good, false); + + comp.set_depth_first (false); + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "net_mismatch INT $10\n" "match_nets OUT OUT\n" "net_mismatch INT2 $11\n" + "match_nets IN IN\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -1266,6 +1305,7 @@ TEST(6_BufferTwoPathsAdditionalDevices) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -1274,9 +1314,9 @@ TEST(6_BufferTwoPathsAdditionalDevices) "match_nets INT $11\n" "net_mismatch VDD VDD\n" "match_nets IN IN\n" + "net_mismatch INT2 $10\n" "net_mismatch VSS VSS\n" "net_mismatch OUT OUT\n" - "net_mismatch INT2 $10\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -1318,6 +1358,7 @@ TEST(7_Resistors) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -1359,6 +1400,7 @@ TEST(7_ResistorsParameterMismatch) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -1401,6 +1443,7 @@ TEST(7_ResistorsPlusOneDevice) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -1443,6 +1486,7 @@ TEST(8_Diodes) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -1484,6 +1528,7 @@ TEST(8_DiodesDontMatchOnSwappedPins) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -1531,6 +1576,7 @@ TEST(10_SimpleSubCircuits) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -1593,6 +1639,7 @@ TEST(10_SimpleSubCircuitsMatchedNames) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); EXPECT_EQ (good, false); @@ -1761,22 +1808,22 @@ TEST(11_MismatchingSubcircuits) // nets are now ambiguous EXPECT_EQ (xref2s (xref), "TOP:TOP [Match]:\n" - " pin $0:$0 [Match]\n" - " pin $1:$1 [Match]\n" - " pin $2:$2 [Match]\n" + " pin $0:$2 [Match]\n" + " pin $1:$0 [Match]\n" + " pin $2:$1 [Match]\n" " pin $3:$3 [Match]\n" - " net IN:OUT [MatchWithWarning]\n" - " pin $0:$0\n" - " subcircuit_pin (null):$1[$3]\n" + " net IN:IN [MatchWithWarning]\n" + " pin $0:$2\n" + " subcircuit_pin (null):$2[$1]\n" " subcircuit_pin $1[$0]:(null)\n" - " net OUT:VDD [MatchWithWarning]\n" - " pin $1:$1\n" + " net OUT:OUT [MatchWithWarning]\n" + " pin $1:$0\n" + " subcircuit_pin (null):$1[$3]\n" + " subcircuit_pin $2[$1]:(null)\n" + " net VDD:VDD [MatchWithWarning]\n" + " pin $2:$1\n" " subcircuit_pin (null):$1[$0]\n" " subcircuit_pin (null):$2[$0]\n" - " subcircuit_pin $2[$1]:(null)\n" - " net VDD:IN [MatchWithWarning]\n" - " pin $2:$2\n" - " subcircuit_pin (null):$2[$1]\n" " subcircuit_pin $1[$2]:(null)\n" " subcircuit_pin $2[$2]:(null)\n" " net VSS:VSS [MatchWithWarning]\n" @@ -1818,6 +1865,7 @@ TEST(12_MismatchingSubcircuitsDuplicates) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -1885,6 +1933,7 @@ TEST(13_MismatchingSubcircuitsAdditionalHierarchy) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -1952,6 +2001,7 @@ TEST(14_Subcircuit2Nand) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1); bool good = comp.compare (&nl1, &nl2); @@ -2026,6 +2076,7 @@ TEST(14_Subcircuit2NandMismatchNoSwap) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); // intentionally missing: comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1); bool good = comp.compare (&nl1, &nl2); @@ -2177,6 +2228,7 @@ TEST(14_Subcircuit2MatchWithSwap) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1); bool good = comp.compare (&nl1, &nl2); @@ -2256,6 +2308,7 @@ TEST(15_EmptySubCircuitTest) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -2331,6 +2384,7 @@ TEST(15_EmptySubCircuitWithoutPinNames) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -2413,6 +2467,7 @@ TEST(16_UniqueSubCircuitMatching) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -2552,8 +2607,8 @@ TEST(17_InherentlyAmbiguousDecoder) "match_ambiguous_nets NQ1 NQ1\n" "match_ambiguous_nets NQ2 NQ2\n" "match_nets NQ3 NQ3\n" - "match_ambiguous_nets NA NA\n" - "match_ambiguous_nets NB NB\n" + "match_nets NA NA\n" + "match_nets NB NB\n" "match_nets B B\n" "match_nets A A\n" "match_pins $0 $1\n" @@ -2657,10 +2712,61 @@ TEST(17_InherentlyAmbiguousDecoder) "match_nets B B\n" "match_nets NB NB\n" "match_nets NA NA\n" - "match_nets NQ0 NQ0\n" - "match_nets NQ2 NQ2\n" "match_nets NQ1 NQ1\n" "match_nets NQ3 NQ3\n" + "match_nets NQ2 NQ2\n" + "match_nets NQ0 NQ0\n" + "match_pins $0 $1\n" + "match_pins $1 $0\n" + "match_pins $2 $2\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" + "match_pins $5 $5\n" + "match_pins $6 $6\n" + "match_pins $7 $7\n" + "match_subcircuits $1 $1\n" + "match_subcircuits $2 $2\n" + "match_subcircuits $4 $3\n" + "match_subcircuits $6 $4\n" + "match_subcircuits $3 $5\n" + "match_subcircuits $5 $6\n" + "end_circuit DECODER DECODER MATCH" + ); + + EXPECT_EQ (good, true); + + comp.set_depth_first (false); + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit NAND NAND\n" + "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_nets OUT OUT\n" + "match_nets VDD VDD\n" + "match_nets B B\n" + "match_nets A A\n" + "match_pins $0 $0\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" + "match_devices $1 $1\n" + "match_devices $2 $2\n" + "match_devices $3 $3\n" + "match_devices $4 $4\n" + "end_circuit NAND NAND MATCH\n" + "begin_circuit DECODER DECODER\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets NA NA\n" + "match_nets NB NB\n" + "match_nets B B\n" + "match_nets NQ1 NQ1\n" + "match_nets NQ3 NQ3\n" + "match_nets NQ2 NQ2\n" + "match_nets NQ0 NQ0\n" "match_pins $0 $1\n" "match_pins $1 $0\n" "match_pins $2 $2\n" @@ -2735,6 +2841,7 @@ TEST(18_ClockTree) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (false); bool good = comp.compare (&nl1, &nl2); @@ -2744,6 +2851,190 @@ TEST(18_ClockTree) txt = tl::replaced (txt, "L", "X"); txt = tl::replaced (txt, "R", "X"); + EXPECT_EQ (txt, + "begin_circuit INV INV\n" + "match_nets VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_nets VSS VSS\n" + "match_pins IN IN\n" + "match_pins OUT OUT\n" + "match_pins VDD VDD\n" + "match_pins VSS VSS\n" + "match_devices $1 $1\n" + "match_devices $2 $2\n" + "end_circuit INV INV MATCH\n" + "begin_circuit TXEE TXEE\n" + "match_nets IN IN\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets S S\n" + "match_nets SX SX\n" + "match_nets SX SX\n" + "match_nets SXX SXX\n" + "match_nets SXX SXX\n" + "match_nets SXXX SXXX\n" + "match_nets SXXX SXXX\n" + "match_nets SXXX SXXX\n" + "match_nets SXXX SXXX\n" + "match_nets SXX SXX\n" + "match_nets SXX SXX\n" + "match_nets SXXX SXXX\n" + "match_nets SXXX SXXX\n" + "match_nets SXXX SXXX\n" + "match_nets SXXX SXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TX TX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXX TXX\n" + "match_subcircuits TXX TXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits T T\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TX TX\n" + "match_subcircuits TXX TXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXX TXX\n" + "end_circuit TXEE TXEE MATCH" + ); + EXPECT_EQ (good, true); + + logger.clear (); + + comp.set_dont_consider_net_names (true); + good = comp.compare (&nl1, &nl2); + + txt = logger.text (); + // because L/R matching is ambiguous, we need to do this to + // establish reproducability on different platforms: + txt = tl::replaced (txt, "L", "X"); + txt = tl::replaced (txt, "R", "X"); + + EXPECT_EQ (txt, + "begin_circuit INV INV\n" + "match_nets VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_nets VSS VSS\n" + "match_pins IN IN\n" + "match_pins OUT OUT\n" + "match_pins VDD VDD\n" + "match_pins VSS VSS\n" + "match_devices $1 $1\n" + "match_devices $2 $2\n" + "end_circuit INV INV MATCH\n" + "begin_circuit TXEE TXEE\n" + "match_nets IN IN\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets S S\n" + "match_ambiguous_nets SX SX\n" + "match_ambiguous_nets SX SX\n" + "match_ambiguous_nets SXX SXX\n" + "match_ambiguous_nets SXX SXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXX SXX\n" + "match_ambiguous_nets SXX SXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TX TX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXX TXX\n" + "match_subcircuits TXX TXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits T T\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TX TX\n" + "match_subcircuits TXX TXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXX TXX\n" + "end_circuit TXEE TXEE MATCH" + ); + EXPECT_EQ (good, true); + + comp.set_depth_first (false); + logger.clear (); + good = comp.compare (&nl1, &nl2); + + txt = logger.text (); + // because L/R matching is ambiguous, we need to do this to + // establish reproducability on different platforms: + txt = tl::replaced (txt, "L", "X"); + txt = tl::replaced (txt, "R", "X"); + + EXPECT_EQ (txt, + "begin_circuit INV INV\n" + "match_nets VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_nets VSS VSS\n" + "match_pins IN IN\n" + "match_pins OUT OUT\n" + "match_pins VDD VDD\n" + "match_pins VSS VSS\n" + "match_devices $1 $1\n" + "match_devices $2 $2\n" + "end_circuit INV INV MATCH\n" + "begin_circuit TXEE TXEE\n" + "match_nets IN IN\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets S S\n" + "match_ambiguous_nets SX SX\n" + "match_ambiguous_nets SX SX\n" + "match_ambiguous_nets SXX SXX\n" + "match_ambiguous_nets SXX SXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXX SXX\n" + "match_ambiguous_nets SXX SXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_ambiguous_nets SXXX SXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TX TX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXX TXX\n" + "match_subcircuits TXX TXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits T T\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TX TX\n" + "match_subcircuits TXX TXX\n" + "match_subcircuits TXXX TXXX\n" + "match_subcircuits TXX TXX\n" + "end_circuit TXEE TXEE MATCH" + ); + EXPECT_EQ (good, true); + + comp.set_depth_first (false); + logger.clear (); + good = comp.compare (&nl1, &nl2); + + txt = logger.text (); + // because L/R matching is ambiguous, we need to do this to + // establish reproducability on different platforms: + txt = tl::replaced (txt, "L", "X"); + txt = tl::replaced (txt, "R", "X"); + EXPECT_EQ (txt, "begin_circuit INV INV\n" "match_nets VDD VDD\n" @@ -2903,6 +3194,7 @@ TEST(19_SymmetricCircuit) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -2922,24 +3214,123 @@ TEST(19_SymmetricCircuit) "match_nets g1 G1\n" "match_nets $44 YI\n" "match_nets $14 WELL\n" - "match_nets $8 NET215\n" - "match_nets $9 NET175\n" - "match_nets $6 NET181\n" - "match_nets $4 NET200\n" - "match_nets nn1 NN1\n" + "match_ambiguous_nets nn2 NN2\n" + "match_ambiguous_nets nn2_ NN2_\n" + "match_ambiguous_nets q0 Q0\n" + "match_ambiguous_nets q1 Q1\n" "match_nets $11 CS0\n" - "match_nets $13 CS1\n" - "match_nets nn2 NN2\n" - "match_nets nn2_ NN2_\n" - "match_nets q0 Q0\n" - "match_nets q1 Q1\n" "match_nets q0_ Q0_\n" + "match_nets $6 NET181\n" + "match_nets nn1 NN1\n" + "match_nets $8 NET215\n" + "match_nets $13 CS1\n" "match_nets q1_ Q1_\n" - "match_nets a0_ A0_\n" - "match_nets $34 HNET48\n" - "match_nets nn1_ NN1_\n" "match_nets a0 A0\n" "match_nets $35 HNET44\n" + "match_nets nn1_ NN1_\n" + "match_nets $9 NET175\n" + "match_nets $4 NET200\n" + "match_nets a0_ A0_\n" + "match_nets $34 HNET48\n" + "match_pins VDD VDD\n" + "match_pins nn1_ NN1_\n" + "match_pins nn1 NN1\n" + "match_pins q0 Q0\n" + "match_pins q0_ Q0_\n" + "match_pins q1_ Q1_\n" + "match_pins q1 Q1\n" + "match_pins nn2 NN2\n" + "match_pins nn2_ NN2_\n" + "match_pins a0 A0\n" + "match_pins a0_ A0_\n" + "match_pins g1 G1\n" + "match_pins g0 G0\n" + "match_pins gtp NN3\n" + "match_pins VSS VSS\n" + "match_pins WELL WELL\n" + "match_devices $30 0\n" + "match_devices $29 1\n" + "match_devices $9 10\n" + "match_devices $10 11\n" + "match_devices $36 12\n" + "match_devices $35 13\n" + "match_devices $34 14\n" + "match_devices $38 15\n" + "match_devices $37 16\n" + "match_devices $33 17\n" + "match_devices $27 18\n" + "match_devices $28 19\n" + "match_devices $17 2\n" + "match_devices $31 20\n" + "match_devices $32 21\n" + "match_devices $22 22\n" + "match_devices $26 23\n" + "match_devices $23 24\n" + "match_devices $43 25\n" + "match_devices $20 26\n" + "match_devices $25 27\n" + "match_devices $15 28\n" + "match_devices $14 29\n" + "match_devices $16 3\n" + "match_devices $18 30\n" + "match_devices $21 31\n" + "match_devices $13 32\n" + "match_devices $19 33\n" + "match_devices $7 34\n" + "match_devices $8 35\n" + "match_devices $24 36\n" + "match_devices $3 37\n" + "match_devices $6 38\n" + "match_devices $4 39\n" + "match_devices $39 4\n" + "match_devices $5 40\n" + "match_devices $2 41\n" + "match_devices $1 42\n" + "match_devices $40 5\n" + "match_devices $11 6\n" + "match_devices $12 7\n" + "match_devices $41 8\n" + "match_devices $42 9\n" + "end_circuit DECODE DECODE MATCH" + ); + EXPECT_EQ (good, true); + + comp.set_depth_first (false); + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit DECODE DECODE\n" + "match_nets $41 WL1_EN_\n" + "match_nets VDD VDD\n" + "match_nets $39 NET194\n" + "match_nets g0 G0\n" + "match_nets $40 HNET52\n" + "match_nets VSS VSS\n" + "match_nets $42 NET189\n" + "match_nets gtp NN3\n" + "match_nets $37 NET193\n" + "match_nets g1 G1\n" + "match_nets $44 YI\n" + "match_nets $14 WELL\n" + "match_ambiguous_nets nn2 NN2\n" + "match_ambiguous_nets nn2_ NN2_\n" + "match_ambiguous_nets q0 Q0\n" + "match_ambiguous_nets q1 Q1\n" + "match_nets $11 CS0\n" + "match_nets q0_ Q0_\n" + "match_nets $4 NET200\n" + "match_nets $13 CS1\n" + "match_nets q1_ Q1_\n" + "match_nets $9 NET175\n" + "match_nets a0 A0\n" + "match_nets a0_ A0_\n" + "match_nets $35 HNET44\n" + "match_nets $34 HNET48\n" + "match_nets $6 NET181\n" + "match_nets $8 NET215\n" + "match_nets nn1 NN1\n" + "match_nets nn1_ NN1_\n" "match_pins VDD VDD\n" "match_pins nn1_ NN1_\n" "match_pins nn1 NN1\n" @@ -3060,6 +3451,7 @@ TEST(20_BusLikeConnections) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -3204,6 +3596,153 @@ TEST(20_BusLikeConnections) "end_circuit TOP TOP MATCH" ); EXPECT_EQ (good, true); + + logger.clear (); + + comp.set_dont_consider_net_names (false); + good = comp.compare (&nl1, &nl2); + + txt = logger.text (); + + EXPECT_EQ (txt, + "begin_circuit INV INV\n" + "match_nets VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_nets VSS VSS\n" + "match_pins IN IN\n" + "match_pins OUT OUT\n" + "match_pins VDD VDD\n" + "match_pins VSS VSS\n" + "match_devices $1 $1\n" + "match_devices $2 $2\n" + "end_circuit INV INV MATCH\n" + "begin_circuit INV8 INV8\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_ambiguous_nets IN1 A1\n" + "match_ambiguous_nets IN2 A2\n" + "match_ambiguous_nets IN3 A3\n" + "match_ambiguous_nets IN4 A4\n" + "match_ambiguous_nets IN5 A5\n" + "match_ambiguous_nets IN6 A6\n" + "match_ambiguous_nets IN7 A7\n" + "match_ambiguous_nets IN8 A8\n" + "match_nets OUT1 Q1\n" + "match_nets OUT2 Q2\n" + "match_nets OUT3 Q3\n" + "match_nets OUT4 Q4\n" + "match_nets OUT5 Q5\n" + "match_nets OUT6 Q6\n" + "match_nets OUT7 Q7\n" + "match_nets OUT8 Q8\n" + "match_pins IN1 A1\n" + "match_pins OUT1 Q1\n" + "match_pins IN2 A2\n" + "match_pins OUT2 Q2\n" + "match_pins IN3 A3\n" + "match_pins OUT3 Q3\n" + "match_pins IN4 A4\n" + "match_pins OUT4 Q4\n" + "match_pins IN5 A5\n" + "match_pins OUT5 Q5\n" + "match_pins IN6 A6\n" + "match_pins OUT6 Q6\n" + "match_pins IN7 A7\n" + "match_pins OUT7 Q7\n" + "match_pins IN8 A8\n" + "match_pins OUT8 Q8\n" + "match_pins VDD VDD\n" + "match_pins VSS VSS\n" + "match_subcircuits I1 I1\n" + "match_subcircuits I8 I8\n" + "match_subcircuits I3 I3\n" + "match_subcircuits I7 I7\n" + "match_subcircuits I4 I4\n" + "match_subcircuits I2 I2\n" + "match_subcircuits I6 I6\n" + "match_subcircuits I5 I5\n" + "end_circuit INV8 INV8 MATCH\n" + "begin_circuit INV8_WRAP INV8_WRAP\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets IN8 A8\n" + "match_nets OUT8 Q8\n" + "match_nets IN7 A7\n" + "match_nets OUT7 Q7\n" + "match_nets IN6 A6\n" + "match_nets OUT6 Q6\n" + "match_nets IN5 A5\n" + "match_nets OUT5 Q5\n" + "match_nets IN4 A4\n" + "match_nets OUT4 Q4\n" + "match_nets IN3 A3\n" + "match_nets OUT3 Q3\n" + "match_nets IN2 A2\n" + "match_nets OUT2 Q2\n" + "match_nets IN1 A1\n" + "match_nets OUT1 Q1\n" + "match_pins IN1 A1\n" + "match_pins OUT1 Q1\n" + "match_pins IN2 A2\n" + "match_pins OUT2 Q2\n" + "match_pins IN3 A3\n" + "match_pins OUT3 Q3\n" + "match_pins IN4 A4\n" + "match_pins OUT4 Q4\n" + "match_pins IN5 A5\n" + "match_pins OUT5 Q5\n" + "match_pins IN6 A6\n" + "match_pins OUT6 Q6\n" + "match_pins IN7 A7\n" + "match_pins OUT7 Q7\n" + "match_pins IN8 A8\n" + "match_pins OUT8 Q8\n" + "match_pins VDD VDD\n" + "match_pins VSS VSS\n" + "match_subcircuits INV8 INV8\n" + "end_circuit INV8_WRAP INV8_WRAP MATCH\n" + "begin_circuit TOP TOP\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets IN8 A8\n" + "match_nets OUT8 Q8\n" + "match_nets IN7 A7\n" + "match_nets OUT7 Q7\n" + "match_nets IN6 A6\n" + "match_nets OUT6 Q6\n" + "match_nets IN5 A5\n" + "match_nets OUT5 Q5\n" + "match_nets IN4 A4\n" + "match_nets OUT4 Q4\n" + "match_nets IN3 A3\n" + "match_nets OUT3 Q3\n" + "match_nets IN2 A2\n" + "match_nets OUT2 Q2\n" + "match_nets IN1 A1\n" + "match_nets OUT1 Q1\n" + "match_pins IN1 A1\n" + "match_pins OUT1 Q1\n" + "match_pins IN2 A2\n" + "match_pins OUT2 Q2\n" + "match_pins IN3 A3\n" + "match_pins OUT3 Q3\n" + "match_pins IN4 A4\n" + "match_pins OUT4 Q4\n" + "match_pins IN5 A5\n" + "match_pins OUT5 Q5\n" + "match_pins IN6 A6\n" + "match_pins OUT6 Q6\n" + "match_pins IN7 A7\n" + "match_pins OUT7 Q7\n" + "match_pins IN8 A8\n" + "match_pins OUT8 Q8\n" + "match_pins VDD VDD\n" + "match_pins VSS VSS\n" + "match_subcircuits INV8 INV8\n" + "end_circuit TOP TOP MATCH" + ); + EXPECT_EQ (good, true); } TEST(21_BusLikeAmbiguousConnections) @@ -3240,6 +3779,7 @@ TEST(21_BusLikeAmbiguousConnections) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -3412,6 +3952,7 @@ TEST(22_NodesRemoved) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -3532,6 +4073,7 @@ TEST(23_NodesRemovedWithError) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -3631,6 +4173,7 @@ TEST(24_NodesRemovedButConnectedInOther) NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); bool good = comp.compare (&nl1, &nl2); @@ -3722,6 +4265,7 @@ TEST(25_JoinSymmetricNets) db::NetlistComparer comp; comp.join_symmetric_nets (nl.circuit_by_name ("NAND2")); + comp.set_dont_consider_net_names (true); // NOTE $1 and $2 are joined because they are symmetric EXPECT_EQ (nl.to_string (), @@ -3760,6 +4304,7 @@ TEST(25b_JoinSymmetricNetsMultiple) db::NetlistComparer comp; comp.join_symmetric_nets (nl.circuit_by_name ("NAND3")); + comp.set_dont_consider_net_names (true); nl.combine_devices (); @@ -3799,17 +4344,18 @@ TEST(25c_JoinSymmetricNetsMultipleMessedUp) db::NetlistComparer comp; comp.join_symmetric_nets (nl.circuit_by_name ("NOR3")); + comp.set_dont_consider_net_names (true); nl.combine_devices (); // NOTE $1 and $2 are joined because they are symmetric EXPECT_EQ (nl.to_string (), "circuit NOR3 (A=A,C=C,B=B,OUT=OUT,VSS=VSS,VDD=VDD);\n" - " device PMOS $1 (S=$5,G=B,D=$3) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $1 (S=$6,G=B,D=$3) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n" " device NMOS $2 (S=OUT,G=A,D=VSS) (L=0.23,W=2.05,AS=0,AD=0,PS=0,PD=0);\n" " device PMOS $3 (S=$3,G=A,D=OUT) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n" " device NMOS $4 (S=OUT,G=B,D=VSS) (L=0.23,W=2.05,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $8 (S=VDD,G=C,D=$5) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $8 (S=VDD,G=C,D=$6) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n" " device NMOS $12 (S=VSS,G=C,D=OUT) (L=0.23,W=2.05,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" ) @@ -3838,6 +4384,7 @@ TEST(26_JoinSymmetricNets) db::NetlistComparer comp; comp.join_symmetric_nets (nl.circuit_by_name ("RESCUBE")); + comp.set_dont_consider_net_names (true); EXPECT_EQ (nl.to_string (), "circuit RESCUBE (A=A,B=B);\n" @@ -3882,6 +4429,7 @@ TEST(27_DontJoinSymmetricNetsWithPins) db::NetlistComparer comp; comp.join_symmetric_nets (nl.circuit_by_name ("NAND2")); + comp.set_dont_consider_net_names (true); // NOTE $1 and $2 are NOT joined because they have pins EXPECT_EQ (nl.to_string (), @@ -3914,6 +4462,8 @@ TEST(28_NoSymmetryDetectionCases) prep_nl (nl, nls); db::NetlistComparer comp; + comp.set_dont_consider_net_names (true); + std::string sref = nl.to_string (); comp.join_symmetric_nets (nl.circuit_by_name ("NAND2")); @@ -3936,6 +4486,8 @@ TEST(28_NoSymmetryDetectionCases) prep_nl (nl, nls); db::NetlistComparer comp; + comp.set_dont_consider_net_names (true); + std::string sref = nl.to_string (); comp.join_symmetric_nets (nl.circuit_by_name ("NAND2")); @@ -3965,6 +4517,7 @@ TEST(28_JoinSymmetricNets) prep_nl (nl, nls); db::NetlistComparer comp; + comp.set_dont_consider_net_names (true); comp.join_symmetric_nets (nl.circuit_by_name ("INV2LOAD")); // NOTE $1 and $2 are joined because they are symmetric diff --git a/src/db/unit_tests/dbStreamLayerTests.cc b/src/db/unit_tests/dbStreamLayerTests.cc index f9338bc74..b0ccf4f10 100644 --- a/src/db/unit_tests/dbStreamLayerTests.cc +++ b/src/db/unit_tests/dbStreamLayerTests.cc @@ -407,3 +407,41 @@ TEST(6) "layer_map('1/0';'3/10-*';'2/0 : 2/0';'2/42 : 2/42';'2/1-41,43-* : */*')" ); } + +// issue #592 +TEST(7) +{ + db::Layout ly; + + unsigned int l1 = ly.insert_layer (db::LayerProperties (85, 0)); + unsigned int l2 = ly.insert_layer (db::LayerProperties (185, 0)); + ly.insert_layer (); + ly.insert_layer (); + + db::LayerMap lm; + lm.map (db::LayerProperties (10001, 0), l1); + lm.map (db::LayerProperties (10000, 0), l2); + + EXPECT_EQ (layers_to_string (ly), "85/0,185/0,,"); + + lm.prepare (ly); + + EXPECT_EQ (layers_to_string (ly), "85/0,185/0,,"); + + std::pair p; + p = lm.logical (db::LayerProperties (85, 0)); + EXPECT_EQ (p.first, false); + EXPECT_EQ (p.second, (unsigned int) 0); + + p = lm.logical (db::LayerProperties (185, 0)); + EXPECT_EQ (p.first, false); + EXPECT_EQ (p.second, (unsigned int) 0); + + p = lm.logical (db::LayerProperties (10000, 0)); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, (unsigned int) 1); + + p = lm.logical (db::LayerProperties (10001, 0)); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, (unsigned int) 0); +} diff --git a/src/gsi/gsi/gsiClassBase.cc b/src/gsi/gsi/gsiClassBase.cc index 670a1466d..776f4ecb3 100644 --- a/src/gsi/gsi/gsiClassBase.cc +++ b/src/gsi/gsi/gsiClassBase.cc @@ -51,6 +51,9 @@ namespace { // we do a initial scan and after this no more write access here. typedef std::map ti_to_class_map_t; static ti_to_class_map_t *sp_ti_to_class = 0; +// NOTE: MacOS/clang seems to have some issue with RTTI across shared objects. This map provides a name-based fallback +typedef std::map tname_to_class_map_t; +static tname_to_class_map_t *sp_tname_to_class = 0; ClassBase::ClassBase (const std::string &doc, const Methods &mm, bool do_register) : m_initialized (false), mp_base (0), mp_parent (0), m_doc (doc), m_methods (mm) @@ -68,6 +71,10 @@ ClassBase::ClassBase (const std::string &doc, const Methods &mm, bool do_registe delete sp_ti_to_class; sp_ti_to_class = 0; } + if (sp_tname_to_class) { + delete sp_tname_to_class; + sp_tname_to_class = 0; + } } } @@ -753,11 +760,18 @@ static void add_class_to_map (const gsi::ClassBase *c) if (! sp_ti_to_class) { sp_ti_to_class = new ti_to_class_map_t (); } + if (! sp_tname_to_class) { + sp_tname_to_class = new tname_to_class_map_t (); + } - if (ti && c->is_of_type (*ti) && !sp_ti_to_class->insert (std::make_pair (ti, c)).second) { - // Duplicate registration of this class - tl::error << "Duplicate registration of class " << c->name () << " (type " << ti->name () << ")"; - tl_assert (false); + if (ti && c->is_of_type (*ti)) { + if (!sp_ti_to_class->insert (std::make_pair (ti, c)).second) { + // Duplicate registration of this class + tl::error << "Duplicate registration of class " << c->name () << " (type " << ti->name () << ")"; + tl_assert (false); + } else { + sp_tname_to_class->insert (std::make_pair (std::string (ti->name ()), c)); + } } } @@ -775,11 +789,19 @@ const ClassBase *class_by_typeinfo_no_assert (const std::type_info &ti) if (! sp_ti_to_class) { return 0; } else { - std::map::const_iterator c = sp_ti_to_class->find (&ti); + ti_to_class_map_t::const_iterator c = sp_ti_to_class->find (&ti); if (c != sp_ti_to_class->end ()) { return c->second; } else { - return 0; + // try name lookup + tname_to_class_map_t::const_iterator cn = sp_tname_to_class->find (std::string (ti.name ())); + if (cn != sp_tname_to_class->end ()) { + // we can use this typeinfo as alias + sp_ti_to_class->insert (std::make_pair (&ti, cn->second)); + return cn->second; + } else { + return 0; + } } } } diff --git a/src/lay/lay/doc/about/lvs_ref_global.xml b/src/lay/lay/doc/about/lvs_ref_global.xml index c1afc715e..f854e06e6 100644 --- a/src/lay/lay/doc/about/lvs_ref_global.xml +++ b/src/lay/lay/doc/about/lvs_ref_global.xml @@ -37,6 +37,15 @@ See Netter#align for a description

See Netter#compare for a description of that function.

+

"consider_net_names" - Indicates whether the netlist comparer shall use net names

+ +

Usage:

+
    +
  • consider_net_names(f)
  • +
+

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

"equivalent_pins" - Marks pins as equivalent

Usage:

diff --git a/src/lay/lay/doc/about/lvs_ref_netter.xml b/src/lay/lay/doc/about/lvs_ref_netter.xml index 1d39215a6..e73991d46 100644 --- a/src/lay/lay/doc/about/lvs_ref_netter.xml +++ b/src/lay/lay/doc/about/lvs_ref_netter.xml @@ -85,6 +85,18 @@ corresponding circuits: the unpaired circuit will be flattened then. This method will return true, if the netlists are equivalent and false otherwise.

+

"consider_net_names" - Indicates whether the netlist comparer shall use net names

+ +

Usage:

+
    +
  • consider_net_names(f)
  • +
+

+If this value is set to true (the default), the netlist comparer +will employ net names to resolve ambiguities. If set to false, +ambiguities will be resolved based on the topology alone. Topology +resolution is more expensive. +

"equivalent_pins" - Marks pins as equivalent

Usage:

diff --git a/src/lay/lay/layMacroEditorDialog.cc b/src/lay/lay/layMacroEditorDialog.cc index 308bf4d8e..1b33d9429 100644 --- a/src/lay/lay/layMacroEditorDialog.cc +++ b/src/lay/lay/layMacroEditorDialog.cc @@ -668,18 +668,21 @@ BEGIN_PROTECTED } } - if (collection && (force_add || (collection->begin () == collection->end () && collection->begin_children () == collection->end_children ()))) { + bool open_template_dialog = false; + if (! force_add && collection && (collection->begin () == collection->end () && collection->begin_children () == collection->end_children ())) { + TipDialog td (this, + tl::to_string (QObject::tr ("To get started with the macro development feature, read the documentation provided:
About Macro Development.")), + "macro-editor-basic-tips"); + open_template_dialog = td.exec_dialog () && td.will_be_shown (); + } + + if (collection && (force_add || open_template_dialog)) { lym::Macro *m = new_macro (); if (force_add && m) { set_run_macro (m); } } - TipDialog td (this, - tl::to_string (QObject::tr ("To get started with the macro development feature, read the documentation provided: About Macro Development.")), - "macro-editor-basic-tips"); - td.exec_dialog (); - } else { if (! cat.empty ()) { diff --git a/src/laybasic/laybasic/AlignCellOptionsDialog.ui b/src/laybasic/laybasic/AlignCellOptionsDialog.ui index 8e556df7b..fb90dd6ac 100644 --- a/src/laybasic/laybasic/AlignCellOptionsDialog.ui +++ b/src/laybasic/laybasic/AlignCellOptionsDialog.ui @@ -7,7 +7,7 @@ 0 0 432 - 342 + 349 @@ -32,7 +32,7 @@ - Put origin relative to cell's bounding box at ... + Place this cell reference point ... @@ -302,6 +302,79 @@ + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ... at x: + + + + + + + + + + µm, y: + + + + + + + + + + µm + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + @@ -322,7 +395,7 @@ Qt::Vertical - QSizePolicy::Fixed + QSizePolicy::Expanding @@ -403,7 +476,10 @@ lb cb rb + x_le + y_le vis_only_cbx + adjust_calls_cbx diff --git a/src/laybasic/laybasic/layBrowseInstancesForm.cc b/src/laybasic/laybasic/layBrowseInstancesForm.cc index a4149cf95..82c08848c 100644 --- a/src/laybasic/laybasic/layBrowseInstancesForm.cc +++ b/src/laybasic/laybasic/layBrowseInstancesForm.cc @@ -702,7 +702,15 @@ BrowseInstancesForm::fill_cell_instances (const db::ICplxTrans &t, const db::Lay std::string aref; if (r > 1 || c > 1) { - aref = tl::sprintf ("[%ld,%ld]", c, r); + aref = "["; + aref += tl::to_string (c); + aref += ","; + aref += tl::to_string (r); + aref += "]"; + } else if (parent_inst.size () > 1) { + aref = "(+"; + aref += tl::to_string (parent_inst.size () - 1); + aref += "x)"; } std::string new_path; diff --git a/src/laybasic/laybasic/layBrowseShapesForm.cc b/src/laybasic/laybasic/layBrowseShapesForm.cc index 24c26737c..fc29b8761 100644 --- a/src/laybasic/laybasic/layBrowseShapesForm.cc +++ b/src/laybasic/laybasic/layBrowseShapesForm.cc @@ -868,7 +868,15 @@ BrowseShapesForm::fill_cell_instances (const db::ICplxTrans &t, const db::Layout std::string aref; if (r > 1 || c > 1) { - aref = tl::sprintf ("[%ld,%ld]", c, r); + aref = "["; + aref += tl::to_string (c); + aref += ","; + aref += tl::to_string (r); + aref += "]"; + } else if (parent_inst.size () > 1) { + aref = "(+"; + aref += tl::to_string (parent_inst.size () - 1); + aref += "x)"; } std::string new_path; diff --git a/src/laybasic/laybasic/layDialogs.cc b/src/laybasic/laybasic/layDialogs.cc index 08715e91e..91d5bb303 100644 --- a/src/laybasic/laybasic/layDialogs.cc +++ b/src/laybasic/laybasic/layDialogs.cc @@ -900,33 +900,39 @@ AlignCellOptionsDialog::~AlignCellOptionsDialog () } bool -AlignCellOptionsDialog::exec_dialog (int &mode_x, int &mode_y, bool &visible_only, bool &adjust_calls) +AlignCellOptionsDialog::exec_dialog (AlignCellOptions &data) { - mp_ui->vis_only_cbx->setChecked (visible_only); - mp_ui->adjust_calls_cbx->setChecked (adjust_calls); + mp_ui->vis_only_cbx->setChecked (data.visible_only); + mp_ui->adjust_calls_cbx->setChecked (data.adjust_parents); QToolButton *buttons[3][3] = { { mp_ui->lb, mp_ui->cb, mp_ui->rb }, { mp_ui->lc, mp_ui->cc, mp_ui->rc }, { mp_ui->lt, mp_ui->ct, mp_ui->rt } }; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { - buttons[i][j]->setChecked (j - 1 == mode_x && i - 1 == mode_y); + buttons[i][j]->setChecked (j - 1 == data.mode_x && i - 1 == data.mode_y); } } + mp_ui->x_le->setText (tl::to_qstring (tl::micron_to_string (data.xpos))); + mp_ui->y_le->setText (tl::to_qstring (tl::micron_to_string (data.ypos))); + if (QDialog::exec ()) { - visible_only = mp_ui->vis_only_cbx->isChecked (); - adjust_calls = mp_ui->adjust_calls_cbx->isChecked (); + data.visible_only = mp_ui->vis_only_cbx->isChecked (); + data.adjust_parents = mp_ui->adjust_calls_cbx->isChecked (); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { if (buttons[i][j]->isChecked ()) { - mode_x = j - 1; - mode_y = i - 1; + data.mode_x = j - 1; + data.mode_y = i - 1; } } } + tl::from_string (tl::to_string (mp_ui->x_le->text ()), data.xpos); + tl::from_string (tl::to_string (mp_ui->y_le->text ()), data.ypos); + return true; } else { @@ -934,7 +940,21 @@ AlignCellOptionsDialog::exec_dialog (int &mode_x, int &mode_y, bool &visible_onl } } -void +void +AlignCellOptionsDialog::accept () +{ +BEGIN_PROTECTED; + + double x = 0.0; + tl::from_string (tl::to_string (mp_ui->x_le->text ()), x); + tl::from_string (tl::to_string (mp_ui->y_le->text ()), x); + + QDialog::accept (); + +END_PROTECTED; +} + +void AlignCellOptionsDialog::button_clicked () { QToolButton *buttons[3][3] = { { mp_ui->lb, mp_ui->cb, mp_ui->rb }, { mp_ui->lc, mp_ui->cc, mp_ui->rc }, { mp_ui->lt, mp_ui->ct, mp_ui->rt } }; diff --git a/src/laybasic/laybasic/layDialogs.h b/src/laybasic/laybasic/layDialogs.h index 2f78430f8..5d471ccab 100644 --- a/src/laybasic/laybasic/layDialogs.h +++ b/src/laybasic/laybasic/layDialogs.h @@ -363,6 +363,21 @@ private: lay::LayoutView *mp_view; }; +/** + * @brief A data structure holding the options for the "align cell" dialog + */ +struct LAYBASIC_PUBLIC AlignCellOptions +{ + AlignCellOptions () + : mode_x (-1), mode_y (-1), xpos (0.0), ypos (0.0), visible_only (false), adjust_parents (true) + { } + + int mode_x, mode_y; + double xpos, ypos; + bool visible_only; + bool adjust_parents; +}; + /** * @brief The merge operation options */ @@ -375,10 +390,11 @@ public: AlignCellOptionsDialog (QWidget *parent); virtual ~AlignCellOptionsDialog (); - bool exec_dialog (int &mode_x, int &mode_y, bool &visible_only, bool &adjust_calls); + bool exec_dialog (AlignCellOptions &data); private slots: void button_clicked (); + void accept (); private: Ui::AlignCellOptionsDialog *mp_ui; diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index 91d0fe807..f7ae519f5 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -51,7 +51,6 @@ #include "layConverters.h" #include "layGridNet.h" #include "layMove.h" -#include "layDialogs.h" #include "layZoomBox.h" #include "layMouseTracker.h" #include "layTipDialog.h" @@ -463,10 +462,6 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/) m_sel_inside_pcells = false; m_move_to_origin_mode_x = 0; m_move_to_origin_mode_y = 0; - m_align_cell_origin_mode_x = -1; - m_align_cell_origin_mode_y = -1; - m_align_cell_origin_visible_layers = false; - m_align_cell_adjust_parents = true; m_del_cell_mode = 0; m_layer_hier_mode = 0; m_add_other_layers = false; @@ -5292,7 +5287,7 @@ LayoutView::cm_align_cell_origin () } lay::AlignCellOptionsDialog dialog (this); - if (dialog.exec_dialog (m_align_cell_origin_mode_x, m_align_cell_origin_mode_y, m_align_cell_origin_visible_layers, m_align_cell_adjust_parents)) { + if (dialog.exec_dialog (m_align_cell_options)) { clear_selection (); @@ -5300,7 +5295,7 @@ LayoutView::cm_align_cell_origin () db::Box bbox; - if (m_align_cell_origin_visible_layers) { + if (m_align_cell_options.visible_only) { for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) { if (! l->has_children () && l->layer_index () >= 0 && l->cellview_index () == cv_index && l->visible (true /*real*/)) { bbox += cell->bbox (l->layer_index ()); @@ -5311,7 +5306,7 @@ LayoutView::cm_align_cell_origin () } db::Coord refx, refy; - switch (m_align_cell_origin_mode_x) { + switch (m_align_cell_options.mode_x) { case -1: refx = bbox.left (); break; @@ -5322,7 +5317,7 @@ LayoutView::cm_align_cell_origin () refx = bbox.center ().x (); break; } - switch (m_align_cell_origin_mode_y) { + switch (m_align_cell_options.mode_y) { case -1: refy = bbox.bottom (); break; @@ -5334,10 +5329,11 @@ LayoutView::cm_align_cell_origin () break; } - db::Trans t (db::Vector (-refx, -refy)); db::Layout &layout = cellview (cv_index)->layout (); db::Cell &nc_cell = layout.cell (cell->cell_index ()); + db::Trans t (db::Vector (-refx + db::coord_traits::rounded (m_align_cell_options.xpos / layout.dbu ()), -refy + db::coord_traits::rounded (m_align_cell_options.ypos / layout.dbu ()))); + for (unsigned int i = 0; i < layout.layers (); ++i) { if (layout.is_valid_layer (i)) { db::Shapes &shapes = nc_cell.shapes (i); @@ -5351,7 +5347,7 @@ LayoutView::cm_align_cell_origin () nc_cell.transform (*inst, t); } - if (m_align_cell_adjust_parents) { + if (m_align_cell_options.adjust_parents) { std::vector > insts_to_modify; for (db::Cell::parent_inst_iterator pi = nc_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index 4f3eb3563..fc05c566e 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -50,6 +50,7 @@ #include "layPlugin.h" #include "layDisplayState.h" #include "layBookmarkList.h" +#include "layDialogs.h" #include "gsi.h" #include "tlException.h" #include "tlEvents.h" @@ -2871,9 +2872,7 @@ private: db::LayerProperties m_new_layer_props; db::DVector m_move_dist; int m_move_to_origin_mode_x, m_move_to_origin_mode_y; - int m_align_cell_origin_mode_x, m_align_cell_origin_mode_y; - bool m_align_cell_origin_visible_layers; - bool m_align_cell_adjust_parents; + lay::AlignCellOptions m_align_cell_options; int m_del_cell_mode; int m_layer_hier_mode; int m_duplicate_hier_mode; diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index d48752965..0aae08e5a 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -809,15 +809,31 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co } else { - // The array (or single instance) must be iterated instance - // by instance - for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ++p) { + size_t qid = 0; + + // The array (or single instance) must be iterated instance by instance + for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) { test_snapshot (0); db::ICplxTrans t (cell_inst.complex_trans (*p)); db::Box new_vp = db::Box (t.inverted () * *v); draw_boxes (drawing_context, new_ci, trans * t, new_vp, level + 1); + if (p.quad_id () > 0 && p.quad_id () != qid) { + + qid = p.quad_id (); + + // if the quad is very small we don't gain anything from looking further into the quad - skip this one + db::DBox qb = trans * cell_inst.quad_box (p, bc); + if (qb.width () < 1.0 && qb.height () < 1.0) { + p.skip_quad (); + continue; + } + + } + + ++p; + } } @@ -986,7 +1002,6 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty ++inst; } - } } @@ -1582,6 +1597,8 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ db::Shape last_array; size_t current_quad_id = 0; + size_t current_array_quad_id = 0; + db::ShapeIterator shape (shapes.begin_touching (*v, db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths, mp_prop_sel, m_inv_prop_sel)); while (! shape.at_end ()) { @@ -1596,16 +1613,18 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ } if (skip) { - shape.skip_quad (); + continue; + } - } else { + if (shape.in_array ()) { - bool simplified = false; - - if (shape.in_array () && last_array != shape.array ()) { + if (last_array != shape.array ()) { last_array = shape.array (); + current_array_quad_id = 0; + + bool simplified = false; if (last_array.type () == db::Shape::PolygonPtrArray) { simplified = draw_array_simplified (mp_renderer.get (), last_array, frame, vertex, trans); @@ -1619,17 +1638,42 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ simplified = draw_array_simplified (mp_renderer.get (), last_array, frame, vertex, trans); } + if (simplified) { + shape.finish_array (); + // continue with the next shape, array or quad + continue; + } + } - if (simplified) { - shape.finish_array (); - } else { - mp_renderer->draw (*shape, trans, fill, frame, vertex, text); - ++shape; + } else { + current_array_quad_id = 0; + } + + // try whether the array quad can be simplified + + size_t aqid = shape.array_quad_id (); + if (aqid != 0 && aqid != current_array_quad_id) { + + current_array_quad_id = aqid; + + db::DBox qbbox = trans * shape.array_quad_box (); + if (qbbox.width () < 1.5 && qbbox.height () < 1.5) { + + // draw a single box instead of the quad + mp_renderer->draw (qbbox, fill, frame, vertex, text); + shape.skip_array_quad (); + + // continue with the next shape, array or quad + continue; + } } + mp_renderer->draw (*shape, trans, fill, frame, vertex, text); + ++shape; + } } @@ -1727,7 +1771,9 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ } else if (anything) { - for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ++p) { + size_t qid = 0; + + for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) { if (! m_draw_array_border_instances || p.index_a () <= 0 || (unsigned long)p.index_a () == amax - 1 || p.index_b () <= 0 || (unsigned long)p.index_b () == bmax - 1) { @@ -1736,6 +1782,21 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ db::Box new_vp = db::Box (t.inverted () * *v); draw_layer (from_level, to_level, new_ci, trans * t, new_vp, level + 1, fill, frame, vertex, text, update_snapshot); + if (p.quad_id () > 0 && p.quad_id () != qid) { + + qid = p.quad_id (); + + // if the quad is very small we don't gain anything from looking further into the quad - skip this one + db::DBox qb = trans * cell_inst.quad_box (p, bc); + if (qb.width () < 1.0 && qb.height () < 1.0) { + p.skip_quad (); + continue; + } + + } + + ++p; + } } diff --git a/src/laybasic/laybasic/layTipDialog.cc b/src/laybasic/laybasic/layTipDialog.cc index b4510d4da..416f64468 100644 --- a/src/laybasic/laybasic/layTipDialog.cc +++ b/src/laybasic/laybasic/layTipDialog.cc @@ -128,12 +128,9 @@ TipDialog::no_pressed () accept (); } -bool -TipDialog::do_exec_dialog (button_type *button) +static std::pair +tip_dialog_status (const std::string &key) { - bool must_show = true; - mp_res = button; - std::string th; if (lay::Dispatcher::instance ()) { lay::Dispatcher::instance ()->config_get (cfg_tip_window_hidden, th); @@ -148,20 +145,39 @@ TipDialog::do_exec_dialog (button_type *button) } int r = -1; ex.test ("=") && ex.try_read (r); - if (k == m_key) { - if (r >= 0) { - *mp_res = button_type (r); - } - must_show = false; - break; + if (k == key) { + return std::make_pair (false, r); } ex.test (","); } - if (must_show) { + return std::make_pair (true, -1); +} + +bool +TipDialog::will_be_shown () +{ + return tip_dialog_status (m_key).first; +} + +bool +TipDialog::do_exec_dialog (button_type *button) +{ + mp_res = button; + + std::string th; + if (lay::Dispatcher::instance ()) { + lay::Dispatcher::instance ()->config_get (cfg_tip_window_hidden, th); + } + + std::pair td_status = tip_dialog_status (m_key); + if (td_status.first) { exec (); return true; } else { + if (td_status.second >= 0) { + *mp_res = button_type (td_status.second); + } return false; } } diff --git a/src/laybasic/laybasic/layTipDialog.h b/src/laybasic/laybasic/layTipDialog.h index 90627304f..4a0a74618 100644 --- a/src/laybasic/laybasic/layTipDialog.h +++ b/src/laybasic/laybasic/layTipDialog.h @@ -65,6 +65,11 @@ public: */ ~TipDialog (); + /** + * @brief Returns true, if the tip dialog will be shown + */ + bool will_be_shown (); + /** * @brief Show the dialog * diff --git a/src/lvs/lvs/built-in-macros/_lvs_engine.rb b/src/lvs/lvs/built-in-macros/_lvs_engine.rb index e1179f3a7..f53bfcc72 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_engine.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_engine.rb @@ -155,6 +155,12 @@ module LVS # @synopsis max_depth(n) # See \Netter#max_depth for a description of that function. + # %LVS% + # @name consider_net_names + # @brief Indicates whether the netlist comparer shall use net names + # @synopsis consider_net_names(f) + # See \Netter#consider_net_names for a description of that function. + # %LVS% # @name tolerance # @brief Specifies compare tolerances for certain device parameters @@ -162,7 +168,7 @@ module LVS # @synopsis tolerance(device_class_name, parameter_name [, :absolute => absolute_tolerance] [, :relative => relative_tolerance]) # See \Netter#tolerance for a description of that function. - %w(schematic compare join_symmetric_nets tolerance align same_nets same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity).each do |f| + %w(schematic compare join_symmetric_nets tolerance align same_nets same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity consider_net_names).each do |f| eval <<"CODE" def #{f}(*args) _netter.#{f}(*args) diff --git a/src/lvs/lvs/built-in-macros/_lvs_netter.rb b/src/lvs/lvs/built-in-macros/_lvs_netter.rb index 28d2ae487..2f4219f69 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_netter.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_netter.rb @@ -631,6 +631,20 @@ module LVS @comparer_config << lambda { |comparer| comparer.max_branch_complexity = v } end + # %LVS% + # @name consider_net_names + # @brief Indicates whether the netlist comparer shall use net names + # @synopsis consider_net_names(f) + # If this value is set to true (the default), the netlist comparer + # will employ net names to resolve ambiguities. If set to false, + # ambiguities will be resolved based on the topology alone. Topology + # resolution is more expensive. + + def consider_net_names(value) + v = ! value + @comparer_config << lambda { |comparer| comparer.dont_consider_net_names = v } + end + end end diff --git a/src/lym/lym/lymMacro.cc b/src/lym/lym/lymMacro.cc index 1cfefd7b1..0c00c2847 100644 --- a/src/lym/lym/lymMacro.cc +++ b/src/lym/lym/lymMacro.cc @@ -251,6 +251,7 @@ void Macro::load_from (const std::string &fn) } m_modified = true; + m_is_file = true; on_changed (); } @@ -293,9 +294,6 @@ void Macro::load_from_string (const std::string &text, const std::string &url) void Macro::load () { load_from (path ()); - m_modified = false; - m_is_file = true; - on_changed (); } bool @@ -449,13 +447,13 @@ void Macro::reset_modified () bool Macro::rename (const std::string &n) { - if (m_is_file) { + if (m_is_file && parent ()) { std::string suffix = suffix_for_format (m_interpreter, m_dsl_interpreter, m_format); if (tl::verbosity () >= 20) { tl::log << "Renaming macro " << path () << " to " << n; } QFile f (tl::to_qstring (path ())); - if (! f.rename (QFileInfo (QDir (tl::to_qstring (mp_parent->path ())), tl::to_qstring (n + suffix)).filePath ())) { + if (! f.rename (QFileInfo (QDir (tl::to_qstring (parent ()->path ())), tl::to_qstring (n + suffix)).filePath ())) { return false; } } @@ -1294,14 +1292,15 @@ MacroCollection::add_folder (const std::string &description, const std::string & begin_changes (); MacroCollection *mc = m_folders.insert (std::make_pair (path, new MacroCollection ())).first->second; - mc->set_parent (this); mc->set_name (path); mc->set_description (description); mc->set_category (cat); mc->set_readonly (readonly); mc->scan (path); + mc->set_parent (this); on_changed (); + on_macro_changed (0); return mc; } @@ -1376,7 +1375,6 @@ void MacroCollection::scan (const std::string &path) } if (! found) { Macro *m = m_macros.insert (std::make_pair (n, new Macro ()))->second; - m->set_parent (this); m->set_interpreter (interpreter); m->set_autorun_default (autorun); m->set_autorun (autorun); @@ -1387,6 +1385,7 @@ void MacroCollection::scan (const std::string &path) m->set_readonly (m_readonly); m->reset_modified (); m->set_is_file (); + m->set_parent (this); } } @@ -1425,6 +1424,7 @@ void MacroCollection::scan (const std::string &path) try { std::string n = tl::to_string (QFileInfo (*f).completeBaseName ()); + std::string mp = tl::to_string (dir.absoluteFilePath (*f)); Macro::Format format = Macro::NoFormat; Macro::Interpreter interpreter = Macro::None; @@ -1451,10 +1451,11 @@ void MacroCollection::scan (const std::string &path) m->set_autorun (autorun); m->set_interpreter (interpreter); m->set_dsl_interpreter (dsl_name); - m->set_parent (this); m->set_name (n); - m->load (); + m->load_from (mp); + m->reset_modified (); m->set_readonly (m_readonly); + m->set_parent (this); } } @@ -1478,12 +1479,12 @@ void MacroCollection::scan (const std::string &path) MacroCollection *&mc = m_folders.insert (std::make_pair (n, (MacroCollection *) 0)).first->second; if (! mc) { mc = new MacroCollection (); - mc->set_parent (this); mc->set_name (n); mc->set_virtual_mode (NotVirtual); bool ro = (m_readonly || ! QFileInfo (dir.filePath (*f)).isWritable ()); mc->set_readonly (ro); mc->scan (tl::to_string (dir.filePath (*f))); + mc->set_parent (this); } } catch (tl::Exception &ex) { diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 0ba72cd21..55b82c1aa 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -354,120 +354,130 @@ GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normal bool is_reg = instance.is_regular_array (a, b, amax, bmax); - db::Trans t = instance.front (); + for (db::CellInstArray::iterator ii = instance.begin (); ! ii.at_end (); ++ii) { - if (normalize) { + db::Trans t = *ii; - // try to normalize orthogonal arrays into "Cadence notation", that is - // column and row vectors are positive in the coordinate system of the - // rotated array. - - if (is_reg) { + if (normalize) { - if (amax < 2) { - a = db::Vector (); - } - if (bmax < 2) { - b = db::Vector (); - } + // try to normalize orthogonal arrays into "Cadence notation", that is + // column and row vectors are positive in the coordinate system of the + // rotated array. - // normalisation only works for orthogonal vectors, parallel to x or y axis, which are not parallel - if ((a.x () == 0 || a.y () == 0) && (b.x () == 0 || b.y () == 0) && !((a.x () != 0 && b.x () != 0) || (a.y () != 0 && b.y () != 0))) { - - db::FTrans fp = db::FTrans(t.rot ()).inverted (); - - a.transform (fp); - b.transform (fp); + if (is_reg) { - db::Vector p; - for (int i = 0; i < 2; ++i) { + if (amax < 2) { + a = db::Vector (); + } + if (bmax < 2) { + b = db::Vector (); + } - db::Vector *q = (i == 0) ? &a : &b; - unsigned long n = (i == 0) ? amax : bmax; + // normalisation only works for orthogonal vectors, parallel to x or y axis, which are not parallel + if ((a.x () == 0 || a.y () == 0) && (b.x () == 0 || b.y () == 0) && !((a.x () != 0 && b.x () != 0) || (a.y () != 0 && b.y () != 0))) { + + db::FTrans fp = db::FTrans(t.rot ()).inverted (); + + a.transform (fp); + b.transform (fp); + + db::Vector p; + for (int i = 0; i < 2; ++i) { + + db::Vector *q = (i == 0) ? &a : &b; + unsigned long n = (i == 0) ? amax : bmax; + + if (n == 0) { + *q = db::Vector (); + } else { + if (q->x () < 0) { + p += db::Vector ((n - 1) * q->x (), 0); + q->set_x (-q->x ()); + } + if (q->y () < 0) { + p += db::Vector (0, (n - 1) * q->y ()); + q->set_y (-q->y ()); + } + } - if (n == 0) { - *q = db::Vector (); - } else { - if (q->x () < 0) { - p += db::Vector ((n - 1) * q->x (), 0); - q->set_x (-q->x ()); - } - if (q->y () < 0) { - p += db::Vector (0, (n - 1) * q->y ()); - q->set_y (-q->y ()); - } } + if (a.x () != 0 || b.y () != 0) { + std::swap (a, b); + std::swap (amax, bmax); + } + + fp = db::FTrans (t.rot ()); + a.transform (fp); + b.transform (fp); + + t = t * db::Trans (p); + } - if (a.x () != 0 || b.y () != 0) { - std::swap (a, b); - std::swap (amax, bmax); - } - - fp = db::FTrans (t.rot ()); - a.transform (fp); - b.transform (fp); - - t = t * db::Trans (p); - } } - } + write_record_size (4); + write_record (is_reg ? sAREF : sSREF); - write_record_size (4); - write_record (is_reg ? sAREF : sSREF); + write_string_record (sSNAME, m_cell_name_map.cell_name (instance.cell_index ())); - write_string_record (sSNAME, m_cell_name_map.cell_name (instance.cell_index ())); + if (t.rot () != 0 || instance.is_complex ()) { - if (t.rot () != 0 || instance.is_complex ()) { + write_record_size (6); + write_record (sSTRANS); + write_short (t.is_mirror () ? 0x8000 : 0); - write_record_size (6); - write_record (sSTRANS); - write_short (t.is_mirror () ? 0x8000 : 0); - - if (instance.is_complex ()) { - write_record_size (4 + 8); - write_record (sMAG); - write_double (instance.complex_trans ().mag ()); - write_record_size (4 + 8); - write_record (sANGLE); - write_double (instance.complex_trans ().angle ()); - } else { - if ((t.rot () % 4) != 0) { + if (instance.is_complex ()) { + db::CellInstArray::complex_trans_type ct = instance.complex_trans (t); + write_record_size (4 + 8); + write_record (sMAG); + write_double (ct.mag ()); write_record_size (4 + 8); write_record (sANGLE); - write_double ((t.rot () % 4) * 90.0); + write_double (ct.angle ()); + } else { + if ((t.rot () % 4) != 0) { + write_record_size (4 + 8); + write_record (sANGLE); + write_double ((t.rot () % 4) * 90.0); + } } + + } + + if (is_reg) { + write_record_size (4 + 2 * 2); + write_record (sCOLROW); + if (amax > 32767 || bmax > 32767) { + throw tl::Exception (tl::to_string (tr ("Cannot write array references with more than 32767 columns or rows to GDS2 streams"))); + } + write_short (std::max ((unsigned long) 1, bmax)); + write_short (std::max ((unsigned long) 1, amax)); + } + + write_record_size (4 + (is_reg ? 3 : 1) * 2 * 4); + write_record (sXY); + write_int (scale (sf, t.disp ().x ())); + write_int (scale (sf, t.disp ().y ())); + + if (is_reg) { + write_int (scale (sf, t.disp ().x () + b.x () * bmax)); + write_int (scale (sf, t.disp ().y () + b.y () * bmax)); + write_int (scale (sf, t.disp ().x () + a.x () * amax)); + write_int (scale (sf, t.disp ().y () + a.y () * amax)); + } + + finish (layout, prop_id); + + if (is_reg) { + // we have already written all instances + break; } } - - if (is_reg) { - write_record_size (4 + 2 * 2); - write_record (sCOLROW); - if (amax > 32767 || bmax > 32767) { - throw tl::Exception (tl::to_string (tr ("Cannot write array references with more than 32767 columns or rows to GDS2 streams"))); - } - write_short (std::max ((unsigned long) 1, bmax)); - write_short (std::max ((unsigned long) 1, amax)); - } - - write_record_size (4 + (is_reg ? 3 : 1) * 2 * 4); - write_record (sXY); - write_int (scale (sf, t.disp ().x ())); - write_int (scale (sf, t.disp ().y ())); - - if (is_reg) { - write_int (scale (sf, t.disp ().x () + b.x () * bmax)); - write_int (scale (sf, t.disp ().y () + b.y () * bmax)); - write_int (scale (sf, t.disp ().x () + a.x () * amax)); - write_int (scale (sf, t.disp ().y () + a.y () * amax)); - } - - finish (layout, prop_id); } void diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc index fea27aad3..0d055eded 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc @@ -1129,6 +1129,40 @@ TEST(117) EXPECT_EQ (pp == poly, true); } +// error on duplicate cell name +TEST(118) +{ + db::Manager m (false); + db::Layout layout_org (&m); + + db::cell_index_type cid1 = layout_org.add_cell ("A"); + db::cell_index_type cid2 = layout_org.add_cell ("B"); + layout_org.rename_cell (cid2, "A"); // creates a duplicate cell + + db::LayerProperties lp; + lp.layer = 1; + lp.datatype = 0; + unsigned int lid = layout_org.insert_layer (lp); + + layout_org.cell (cid1).shapes (lid).insert (db::Box (0, 0, 1000, 2000)); + layout_org.cell (cid2).shapes (lid).insert (db::Box (0, 0, 1000, 2000)); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_117.gds"); + + bool error = false; + try { + tl::OutputStream stream (tmp_file); + db::SaveLayoutOptions options; + db::Writer writer (options); + writer.write (layout_org, stream); + } catch (tl::Exception &ex) { + tl::warn << ex.msg (); + error = true; + } + + EXPECT_EQ (error, true); +} + // Extreme fracturing by max. points TEST(120) { @@ -1153,3 +1187,4 @@ TEST(166) opt.max_vertex_count = 4; run_test (_this, "t166.oas.gz", "t166_au.gds.gz", false, opt); } + diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 029dc7f5a..30c87b8c2 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -2018,6 +2018,8 @@ OASISReader::do_read_placement (unsigned char r, db::Vector pos (mm_placement_x.get (), mm_placement_y.get ()); + const std::vector *points = 0; + if ((m & 0x8) && read_repetition ()) { std::pair pp = read_element_properties (layout.properties_repository (), false); @@ -2042,6 +2044,42 @@ OASISReader::do_read_placement (unsigned char r, instances.push_back (inst); } + } else if (! layout.is_editable () && (points = mm_repetition.get ().is_iterated ()) != 0) { + + db::CellInstArray inst; + + if (mag_set || angle < 0) { + + db::ICplxTrans ct (mag, angle_deg, mirror, pos); + + db::CellInstArray::iterated_complex_array_type array (ct.rcos (), ct.mag ()); + array.reserve (points->size () + 1); + array.insert (db::Vector ()); + array.insert (points->begin (), points->end ()); + array.sort (); + + inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), + db::Trans (ct), layout.array_repository ().insert (array)); + + } else { + + db::CellInstArray::iterated_array_type array; + array.reserve (points->size () + 1); + array.insert (db::Vector ()); + array.insert (points->begin (), points->end ()); + array.sort (); + + inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), + db::Trans (angle, mirror, pos), layout.array_repository ().insert (array)); + + } + + if (pp.first) { + instances_with_props.push_back (db::CellInstArrayWithProperties (inst, pp.second)); + } else { + instances.push_back (inst); + } + } else { RepetitionIterator p = mm_repetition.get ().begin (); diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index d7d54a313..7791b9e98 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1965,11 +1965,35 @@ OASISWriter::write (const db::CellInstArray &inst, db::properties_id_type prop_i { m_progress.set (mp_stream->pos ()); + std::vector pts; db::Vector a, b; unsigned long amax, bmax; - bool is_reg = inst.is_regular_array (a, b, amax, bmax); - if (is_reg && (amax > 1 || bmax > 1)) { + if (inst.is_iterated_array (&pts) && pts.size () > 1) { + + // Remove the first point which is implicitly contained in the repetition + // Note: we can do so because below we instantiate the shape at the front of the array which includes + // the first transformation already. + db::Vector po = pts.front (); + std::vector::iterator pw = pts.begin(); + for (std::vector::iterator p = pw + 1; p != pts.end (); ++p) { + *pw++ = *p - po; + } + pts.erase (pw, pts.end ()); + + db::IrregularRepetition *rep_base = new db::IrregularRepetition (); + rep_base->points ().swap (pts); + db::Repetition array_rep (rep_base); + + if (rep != db::Repetition ()) { + for (db::RepetitionIterator r = rep.begin (); ! r.at_end (); ++r) { + write_inst_with_rep (inst, prop_id, *r, array_rep); + } + } else { + write_inst_with_rep (inst, prop_id, db::Vector (), array_rep); + } + + } else if (inst.is_regular_array (a, b, amax, bmax) && (amax > 1 || bmax > 1)) { // we cannot use the repetition - instead we write every single instance and use the repetition // for the array information diff --git a/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.h b/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.h index 4010823f4..948553e4f 100644 --- a/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.h +++ b/src/plugins/tools/bool/lay_plugin/layBooleanOperationsDialogs.h @@ -21,8 +21,8 @@ */ -#ifndef HDR_layDialogs -#define HDR_layDialogs +#ifndef HDR_layBooleanOperationsDialogs +#define HDR_layBooleanOperationsDialogs #include "ui_BooleanOptionsDialog.h" #include "ui_SizingOptionsDialog.h" diff --git a/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc b/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc index 208856ef2..173383945 100644 --- a/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc +++ b/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc @@ -224,6 +224,10 @@ RdbDifferenceReceiver::produce_cell_inst (const db::CellInstArrayWithProperties unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { r += tl::sprintf (" [a=%s, b=%s, na=%ld, nb=%ld]", a.to_string (), b.to_string (), amax, bmax); + } else if (ci.size () > 1) { + r += " (+"; + r += tl::to_string (ci.size () - 1); + r += " irregular placements)"; } item->add_value (r); diff --git a/src/pya/pya/pyaObject.cc b/src/pya/pya/pyaObject.cc index c4f8f815c..11ee0be14 100644 --- a/src/pya/pya/pyaObject.cc +++ b/src/pya/pya/pyaObject.cc @@ -306,7 +306,11 @@ PYAObjectBase::detach () } } - detach_callbacks (); + // NOTE: m_owned = false might mean the C++ object is already destroyed. We must not + // modify in this case and without is_managed() there is no way of knowing the state. + if (m_owned) { + detach_callbacks (); + } m_obj = 0; m_const_ref = false; diff --git a/src/rba/rba/rbaInternal.cc b/src/rba/rba/rbaInternal.cc index 8a7ed7fc2..3ca0c0ea2 100644 --- a/src/rba/rba/rbaInternal.cc +++ b/src/rba/rba/rbaInternal.cc @@ -402,7 +402,11 @@ Proxy::detach () } } - clear_callbacks (); + // NOTE: m_owned = false might mean the C++ object is already destroyed. We must not + // modify in this case and without is_managed() there is no way of knowing the state. + if (m_owned) { + clear_callbacks (); + } m_self = Qnil; m_obj = 0; diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index e6d8d2b6f..83271e5d7 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -1019,7 +1019,7 @@ InputPipe::read (char *b, size_t n) size_t ret = fread (b, 1, n, m_file); if (ret < n) { if (ferror (m_file)) { - throw FilePReadErrorException (m_source, ferror (m_file)); + throw FilePReadErrorException (m_source, errno); } } @@ -1049,7 +1049,7 @@ OutputPipe::OutputPipe (const std::string &path) OutputPipe::~OutputPipe () { if (m_file != NULL) { - fclose (m_file); + _pclose (m_file); m_file = NULL; } } @@ -1061,7 +1061,7 @@ OutputPipe::write (const char *b, size_t n) size_t ret = fwrite (b, 1, n, m_file); if (ret < n) { if (ferror (m_file)) { - throw FilePWriteErrorException (m_source, ferror (m_file)); + throw FilePWriteErrorException (m_source, errno); } } } @@ -1106,10 +1106,22 @@ size_t InputPipe::read (char *b, size_t n) { tl_assert (m_file != NULL); - size_t ret = fread (b, 1, n, m_file); - if (ret < n) { - if (ferror (m_file)) { - throw FilePReadErrorException (m_source, ferror (m_file)); + + bool retry = true; + size_t ret = 0; + + while (retry) { + retry = false; + ret = fread (b, 1, n, m_file); + if (ret < n) { + if (ferror (m_file)) { + if (errno != EINTR) { + throw FilePReadErrorException (m_source, errno); + } else if (ret == 0) { + retry = true; + clearerr (m_file); + } + } } } @@ -1138,7 +1150,7 @@ OutputPipe::OutputPipe (const std::string &path) OutputPipe::~OutputPipe () { if (m_file != NULL) { - fclose (m_file); + pclose (m_file); m_file = NULL; } } @@ -1147,10 +1159,11 @@ void OutputPipe::write (const char *b, size_t n) { tl_assert (m_file != NULL); + size_t ret = fwrite (b, 1, n, m_file); if (ret < n) { - if (ferror (m_file)) { - throw FilePWriteErrorException (m_source, ferror (m_file)); + if (ferror (m_file) && errno != EINTR) { + throw FilePReadErrorException (m_source, errno); } } } diff --git a/src/tl/tl/tlUnitTest.cc b/src/tl/tl/tlUnitTest.cc index ecebd781f..649d12d04 100644 --- a/src/tl/tl/tlUnitTest.cc +++ b/src/tl/tl/tlUnitTest.cc @@ -232,6 +232,7 @@ bool TestBase::do_test (bool editable, bool slow) { m_editable = editable; m_slow = slow; + m_any_failed = false; // Ensures the test temp directory is present std::string tmpdir = tl::combine_path (tl::absolute_file_path (testtmp ()), m_testdir); diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc index 733db501d..f2fb84218 100644 --- a/src/tl/unit_tests/tlStreamTests.cc +++ b/src/tl/unit_tests/tlStreamTests.cc @@ -52,6 +52,23 @@ TEST(InputPipe2) EXPECT_NE (ret, 0); } +TEST(OutputPipe1) +{ + std::string tf = tmp_file ("pipe_out"); + + { + tl::OutputPipe pipe ("cat >" + tf); + tl::OutputStream str (pipe); + str << "Hello, world!"; + } + + { + tl::InputStream is (tf); + std::string s = is.read_all (); + EXPECT_EQ (s, "Hello, world!"); + } +} + TEST(TextOutputStream) { std::string fn = tmp_file ("test.txt"); diff --git a/src/unit_tests/unit_test_main.cc b/src/unit_tests/unit_test_main.cc index cca0f4281..11bfc702d 100644 --- a/src/unit_tests/unit_test_main.cc +++ b/src/unit_tests/unit_test_main.cc @@ -544,9 +544,11 @@ main_cont (int &argc, char **argv) bool debug_mode = false; bool continue_flag = false; int repeat = 1; + std::string output; tl::CommandLineOptions cmd; cmd << tl::arg ("-a", &xml_format, "Provide XML output format (JUnit format)") + << tl::arg ("-o=log", &output, "Sends output to the given file") << tl::arg ("-l", &list_tests, "Lists tests and exits") << tl::arg ("-e", &editable, "Uses editable mode") << tl::arg ("-ne", &non_editable, "Uses non-editable mode") @@ -596,8 +598,18 @@ main_cont (int &argc, char **argv) tl::set_continue_flag (continue_flag); tl::set_debug_mode (debug_mode); + FILE *output_file = 0; + try { + if (! output.empty ()) { + output_file = fopen (output.c_str (), "w"); + if (! output_file) { + throw tl::Exception (std::string ("Unable to open log file for writing :") + output); + } + console.send_to (output_file); + } + ut::ctrl << ""; ut::ctrl << ""; @@ -652,8 +664,20 @@ main_cont (int &argc, char **argv) ut::ctrl << ""; + if (output_file) { + console.send_to (stdout); + fclose (output_file); + } + } catch (...) { + ut::ctrl << ""; + + if (output_file) { + console.send_to (stdout); + fclose (output_file); + } + throw; } diff --git a/src/unit_tests/utTestConsole.cc b/src/unit_tests/utTestConsole.cc index b7019a186..7726bc948 100644 --- a/src/unit_tests/utTestConsole.cc +++ b/src/unit_tests/utTestConsole.cc @@ -197,10 +197,25 @@ TestConsole::TestConsole (FILE *file) { ms_instance = this; + prepare_file (); + redirect (); +} + +TestConsole::~TestConsole () +{ + restore (); + + if (ms_instance == this) { + ms_instance = 0; + } +} + +void TestConsole::prepare_file () +{ #if defined(_MSC_VER) m_file_is_tty = false; #else - m_file_is_tty = isatty (fileno (file)); + m_file_is_tty = isatty (fileno (m_file)); #endif #if !defined(_WIN32) @@ -211,16 +226,15 @@ TestConsole::TestConsole (FILE *file) m_rows = std::max (0, (int) ws.ws_row); } #endif - - redirect (); } -TestConsole::~TestConsole () +void +TestConsole::send_to (FILE *file) { - restore (); - - if (ms_instance == this) { - ms_instance = 0; + if (file != m_file) { + flush (); + m_file = file; + prepare_file (); } } diff --git a/src/unit_tests/utTestConsole.h b/src/unit_tests/utTestConsole.h index 6ee323947..46a3dff60 100644 --- a/src/unit_tests/utTestConsole.h +++ b/src/unit_tests/utTestConsole.h @@ -52,6 +52,8 @@ public: TestConsole (FILE *file); ~TestConsole (); + void send_to (FILE *file); + void write_str (const char *text, output_stream os); void raw_write (const char *text); virtual void flush (); @@ -85,6 +87,7 @@ private: void redirect (); void restore (); + void prepare_file (); }; } diff --git a/testdata/algo/issue-609.oas.gz b/testdata/algo/issue-609.oas.gz new file mode 100644 index 000000000..c35964b7b Binary files /dev/null and b/testdata/algo/issue-609.oas.gz differ diff --git a/testdata/algo/lvs_test2_au.lvsdb.1 b/testdata/algo/lvs_test2_au.lvsdb.1 index 017e27c7e..52e53e669 100644 --- a/testdata/algo/lvs_test2_au.lvsdb.1 +++ b/testdata/algo/lvs_test2_au.lvsdb.1 @@ -628,55 +628,46 @@ xref( xref( net(2 2 mismatch) net(3 3 mismatch) - net(5 4 match) - net(4 5 mismatch) + net(4 4 mismatch) + net(5 5 mismatch) net(6 6 match) net(7 7 mismatch) net(1 1 mismatch) pin(1 1 match) pin(2 2 match) - pin(4 3 match) - pin(3 4 match) + pin(3 3 match) + pin(4 4 match) pin(5 5 match) pin(6 6 match) pin(0 0 match) - circuit(2 () mismatch) - circuit(1 1 mismatch) + circuit(1 () mismatch) + circuit(2 1 match) ) ) circuit(RINGO RINGO nomatch xref( - net(() 7 mismatch) - net(() 9 mismatch) - net(() 11 mismatch) - net(() 6 mismatch) - net(() 8 mismatch) - net(5 () mismatch) - net(6 () mismatch) - net(7 () mismatch) - net(9 () mismatch) - net(11 () mismatch) - net(12 () mismatch) - net(10 1 mismatch) - net(8 10 mismatch) - net(1 5 mismatch) + net(8 () mismatch) + net(7 7 match) + net(6 9 match) + net(5 11 match) + net(9 6 match) + net(10 5 match) + net(11 10 match) + net(12 8 match) + net(1 1 mismatch) net(2 2 match) net(3 3 match) net(4 4 match) - pin(() 0 match) - pin(0 () match) + pin(0 0 match) pin(1 1 match) pin(2 2 match) pin(3 3 match) circuit(() 2 mismatch) - circuit(() 3 mismatch) - circuit(() 4 mismatch) - circuit(() 5 mismatch) circuit(2 () mismatch) - circuit(3 () mismatch) - circuit(4 () mismatch) - circuit(5 () mismatch) circuit(1 1 match) + circuit(3 3 match) + circuit(4 4 match) + circuit(5 5 match) ) ) ) diff --git a/testdata/algo/lvs_test2_au.lvsdb.2 b/testdata/algo/lvs_test2_au.lvsdb.2 index 3719b24c1..a73de534d 100644 --- a/testdata/algo/lvs_test2_au.lvsdb.2 +++ b/testdata/algo/lvs_test2_au.lvsdb.2 @@ -628,55 +628,46 @@ xref( xref( net(2 2 mismatch) net(3 3 mismatch) - net(5 4 match) - net(4 5 mismatch) + net(4 4 mismatch) + net(5 5 mismatch) net(6 6 match) net(7 7 mismatch) net(1 1 mismatch) pin(1 1 match) pin(2 2 match) - pin(4 3 match) - pin(3 4 match) + pin(3 3 match) + pin(4 4 match) pin(5 5 match) pin(6 6 match) pin(0 0 match) - circuit(2 () mismatch) - circuit(1 1 mismatch) + circuit(1 () mismatch) + circuit(2 1 match) ) ) circuit(RINGO RINGO nomatch xref( - net(() 7 mismatch) - net(() 9 mismatch) - net(() 11 mismatch) - net(() 6 mismatch) - net(() 8 mismatch) - net(5 () mismatch) - net(6 () mismatch) - net(7 () mismatch) - net(9 () mismatch) - net(11 () mismatch) - net(12 () mismatch) - net(10 1 mismatch) - net(8 10 mismatch) - net(1 5 mismatch) + net(8 () mismatch) + net(7 7 match) + net(6 9 match) + net(5 11 match) + net(9 6 match) + net(10 5 match) + net(11 10 match) + net(12 8 match) + net(1 1 mismatch) net(2 2 match) net(3 3 match) net(4 4 match) - pin(() 0 match) - pin(0 () match) + pin(0 0 match) pin(1 1 match) pin(2 2 match) pin(3 3 match) circuit(() 2 mismatch) - circuit(() 3 mismatch) - circuit(() 4 mismatch) - circuit(() 5 mismatch) circuit(2 () mismatch) - circuit(3 () mismatch) - circuit(4 () mismatch) - circuit(5 () mismatch) circuit(1 1 match) + circuit(3 3 match) + circuit(4 4 match) + circuit(5 5 match) ) ) ) diff --git a/testdata/algo/lvs_test2b_au.lvsdb.1 b/testdata/algo/lvs_test2b_au.lvsdb.1 index 4040bfa7e..a3bbd637b 100644 --- a/testdata/algo/lvs_test2b_au.lvsdb.1 +++ b/testdata/algo/lvs_test2b_au.lvsdb.1 @@ -628,55 +628,46 @@ xref( xref( net(2 2 mismatch) net(3 3 mismatch) - net(5 4 match) - net(4 5 mismatch) + net(4 4 mismatch) + net(5 5 mismatch) net(6 6 match) net(7 7 mismatch) net(1 1 mismatch) pin(1 1 match) pin(2 2 match) - pin(4 3 match) - pin(3 4 match) + pin(3 3 match) + pin(4 4 match) pin(5 5 match) pin(6 6 match) pin(0 0 match) - circuit(2 () mismatch) - circuit(1 1 mismatch) + circuit(1 () mismatch) + circuit(2 1 match) ) ) circuit(RINGO RINGO nomatch xref( - net(() 7 mismatch) - net(() 9 mismatch) - net(() 11 mismatch) - net(() 6 mismatch) - net(() 8 mismatch) - net(5 () mismatch) - net(6 () mismatch) - net(7 () mismatch) - net(9 () mismatch) - net(11 () mismatch) - net(12 () mismatch) - net(10 1 mismatch) - net(8 10 mismatch) - net(1 5 mismatch) + net(8 () mismatch) + net(7 7 match) + net(6 9 match) + net(5 11 match) + net(9 6 match) + net(10 5 match) + net(11 10 match) + net(12 8 match) + net(1 1 mismatch) net(2 2 match) net(3 3 match) net(4 4 match) - pin(() 0 match) - pin(0 () match) + pin(0 0 match) pin(1 1 match) pin(2 2 match) pin(3 3 match) circuit(() 2 mismatch) - circuit(() 3 mismatch) - circuit(() 4 mismatch) - circuit(() 5 mismatch) circuit(2 () mismatch) - circuit(3 () mismatch) - circuit(4 () mismatch) - circuit(5 () mismatch) circuit(1 1 match) + circuit(3 3 match) + circuit(4 4 match) + circuit(5 5 match) ) ) ) diff --git a/testdata/algo/lvs_test2b_au.lvsdb.2 b/testdata/algo/lvs_test2b_au.lvsdb.2 index 59b791eab..8a9639fac 100644 --- a/testdata/algo/lvs_test2b_au.lvsdb.2 +++ b/testdata/algo/lvs_test2b_au.lvsdb.2 @@ -628,55 +628,46 @@ xref( xref( net(2 2 mismatch) net(3 3 mismatch) - net(5 4 match) - net(4 5 mismatch) + net(4 4 mismatch) + net(5 5 mismatch) net(6 6 match) net(7 7 mismatch) net(1 1 mismatch) pin(1 1 match) pin(2 2 match) - pin(4 3 match) - pin(3 4 match) + pin(3 3 match) + pin(4 4 match) pin(5 5 match) pin(6 6 match) pin(0 0 match) - circuit(2 () mismatch) - circuit(1 1 mismatch) + circuit(1 () mismatch) + circuit(2 1 match) ) ) circuit(RINGO RINGO nomatch xref( - net(() 7 mismatch) - net(() 9 mismatch) - net(() 11 mismatch) - net(() 6 mismatch) - net(() 8 mismatch) - net(5 () mismatch) - net(6 () mismatch) - net(7 () mismatch) - net(9 () mismatch) - net(11 () mismatch) - net(12 () mismatch) - net(10 1 mismatch) - net(8 10 mismatch) - net(1 5 mismatch) + net(8 () mismatch) + net(7 7 match) + net(6 9 match) + net(5 11 match) + net(9 6 match) + net(10 5 match) + net(11 10 match) + net(12 8 match) + net(1 1 mismatch) net(2 2 match) net(3 3 match) net(4 4 match) - pin(() 0 match) - pin(0 () match) + pin(0 0 match) pin(1 1 match) pin(2 2 match) pin(3 3 match) circuit(() 2 mismatch) - circuit(() 3 mismatch) - circuit(() 4 mismatch) - circuit(() 5 mismatch) circuit(2 () mismatch) - circuit(3 () mismatch) - circuit(4 () mismatch) - circuit(5 () mismatch) circuit(1 1 match) + circuit(3 3 match) + circuit(4 4 match) + circuit(5 5 match) ) ) ) diff --git a/testdata/lvs/double_height.lvsdb b/testdata/lvs/double_height.lvsdb index 0d2517e42..7a1634dd0 100644 --- a/testdata/lvs/double_height.lvsdb +++ b/testdata/lvs/double_height.lvsdb @@ -295,8 +295,8 @@ Z( Z( N(2 3 1) N(3 5 1) - N(4 4 W) - N(5 6 W) + N(4 4 1) + N(5 6 1) N(1 1 1) N(6 2 1) P(1 2 1) diff --git a/testdata/lvs/double_height2.lvs b/testdata/lvs/double_height2.lvs index 23edbf991..cac92ec75 100644 --- a/testdata/lvs/double_height2.lvs +++ b/testdata/lvs/double_height2.lvs @@ -131,5 +131,7 @@ connect_implicit("DOESNOTEXIST", "DOESNOTEXIST") netlist.simplify align +consider_net_names(false) + compare diff --git a/testdata/lvs/double_height2.lvsdb b/testdata/lvs/double_height2.lvsdb index f9d933d9c..b75ed63bc 100644 --- a/testdata/lvs/double_height2.lvsdb +++ b/testdata/lvs/double_height2.lvsdb @@ -314,8 +314,8 @@ Z( N(5 2 1) N(4 1 1) N(2 4 1) - P(2 () 1) P(0 () 1) + P(2 () 1) P(4 () 1) P(3 () 1) P(1 () 1) diff --git a/testdata/lvs/double_height2_texts.lvs b/testdata/lvs/double_height2_texts.lvs index 34eca592b..33ae20e7f 100644 --- a/testdata/lvs/double_height2_texts.lvs +++ b/testdata/lvs/double_height2_texts.lvs @@ -131,5 +131,7 @@ connect_implicit("DOESNOTEXIST", "DOESNOTEXIST") netlist.simplify align +consider_net_names(false) + compare diff --git a/testdata/lvs/double_height2_texts.lvsdb b/testdata/lvs/double_height2_texts.lvsdb index b40026de2..d62e4e90c 100644 --- a/testdata/lvs/double_height2_texts.lvsdb +++ b/testdata/lvs/double_height2_texts.lvsdb @@ -314,8 +314,8 @@ Z( N(5 2 1) N(4 1 1) N(2 4 1) - P(2 () 1) P(0 () 1) + P(2 () 1) P(4 () 1) P(3 () 1) P(1 () 1) diff --git a/testdata/lvs/floating.lvs b/testdata/lvs/floating.lvs index 2bdc842f1..8a3c6417e 100644 --- a/testdata/lvs/floating.lvs +++ b/testdata/lvs/floating.lvs @@ -71,5 +71,7 @@ connect_global(ptie, "SUBSTRATE") netlist.simplify align +consider_net_names(false) + compare diff --git a/testdata/lvs/invchain_cheat.lvs b/testdata/lvs/invchain_cheat.lvs index d0fd20237..93c6206e7 100644 --- a/testdata/lvs/invchain_cheat.lvs +++ b/testdata/lvs/invchain_cheat.lvs @@ -126,5 +126,7 @@ connect_global(bulk, "SUBSTRATE") netlist.simplify align +consider_net_names(false) + compare diff --git a/testdata/lvs/ringo_simple_blackboxing.lvs b/testdata/lvs/ringo_simple_blackboxing.lvs index 475262be3..2fe02fd83 100644 --- a/testdata/lvs/ringo_simple_blackboxing.lvs +++ b/testdata/lvs/ringo_simple_blackboxing.lvs @@ -83,5 +83,7 @@ netlist.purge netlist.combine_devices netlist.purge_nets +consider_net_names(false) + compare diff --git a/testdata/lvs/ringo_simple_dmos.lvsdb.1 b/testdata/lvs/ringo_simple_dmos.lvsdb.1 index 47ac96fa8..f68490049 100644 --- a/testdata/lvs/ringo_simple_dmos.lvsdb.1 +++ b/testdata/lvs/ringo_simple_dmos.lvsdb.1 @@ -867,8 +867,8 @@ xref( pin(6 6 match) pin(0 0 match) pin(2 2 match) - device(4 4 match) device(3 3 mismatch) + device(4 4 match) device(2 2 match) device(1 1 mismatch) ) diff --git a/testdata/lvs/ringo_simple_dmos.lvsdb.2 b/testdata/lvs/ringo_simple_dmos.lvsdb.2 index 45fc263a5..bd0320264 100644 --- a/testdata/lvs/ringo_simple_dmos.lvsdb.2 +++ b/testdata/lvs/ringo_simple_dmos.lvsdb.2 @@ -867,8 +867,8 @@ xref( pin(6 6 match) pin(0 0 match) pin(2 2 match) - device(4 4 match) device(3 3 mismatch) + device(4 4 match) device(2 2 match) device(1 1 mismatch) ) diff --git a/testdata/net_tracer/t6_all_nets.oas.gz.4 b/testdata/net_tracer/t6_all_nets.oas.gz.4 new file mode 100644 index 000000000..f5aba5bda Binary files /dev/null and b/testdata/net_tracer/t6_all_nets.oas.gz.4 differ diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index ccacfe280..97e97e794 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -1402,6 +1402,12 @@ class Basic_TestClass < TestBase a._manage end + # Looks like Ruby is keeping the last A instance in some kind of cache: + # this will release it + a = RBA::A.new + a._destroy + a = nil + # makes sure the objects inside the block before are deleted GC.start diff --git a/testdata/ruby/dbCellInstArrayTest.rb b/testdata/ruby/dbCellInstArrayTest.rb index 7f2e03f4d..fb61fedc7 100644 --- a/testdata/ruby/dbCellInstArrayTest.rb +++ b/testdata/ruby/dbCellInstArrayTest.rb @@ -81,9 +81,9 @@ class DBCellInst_TestClass < TestBase assert_equal(at.trans.to_s, "r90 0,0") assert_equal(at.cplx_trans.to_s, "r135 *1 0,0") - assert_equal(at < a, true) + assert_equal(at < a, false) assert_equal(at < atdup, false) - assert_equal(a < at, false) + assert_equal(a < at, true) assert_equal(atdup < at, false) assert_equal(a != at, true) assert_equal(a == at, false) @@ -143,9 +143,9 @@ class DBCellInst_TestClass < TestBase assert_equal(at.cplx_trans.to_s, "r135 *1 0,0") assert_equal(at.to_s, "#0 r135 *1 0,0 [-20,10*3;-40,30*5]") - assert_equal(at < a, true) + assert_equal(at < a, false) assert_equal(at < atdup, false) - assert_equal(a < at, false) + assert_equal(a < at, true) assert_equal(atdup < at, false) assert_equal(a != at, true) assert_equal(a == at, false) @@ -210,9 +210,9 @@ class DBCellInst_TestClass < TestBase assert_equal(at.trans.to_s, "r90 0,0") assert_equal(at.cplx_trans.to_s, "r135 *1 0,0") - assert_equal(at < a, true) + assert_equal(at < a, false) assert_equal(at < atdup, false) - assert_equal(a < at, false) + assert_equal(a < at, true) assert_equal(atdup < at, false) assert_equal(a != at, true) assert_equal(a == at, false) @@ -272,9 +272,9 @@ class DBCellInst_TestClass < TestBase assert_equal(at.cplx_trans.to_s, "r135 *1 0,0") assert_equal(at.to_s, "#0 r135 *1 0,0 [-20,10*3;-40,30*5]") - assert_equal(at < a, true) + assert_equal(at < a, false) assert_equal(at < atdup, false) - assert_equal(a < at, false) + assert_equal(a < at, true) assert_equal(atdup < at, false) assert_equal(a != at, true) assert_equal(a == at, false) diff --git a/testdata/ruby/dbLayoutTest.rb b/testdata/ruby/dbLayoutTest.rb index d8b78503b..6b47d6cd2 100644 --- a/testdata/ruby/dbLayoutTest.rb +++ b/testdata/ruby/dbLayoutTest.rb @@ -1122,6 +1122,59 @@ class DBLayout_TestClass < TestBase end + # Cell#transform and Cell#transform_into + def test_14 + + g = RBA::Layout::new + c0 = g.create_cell("c0") + c1 = g.create_cell("c1") + + t = RBA::Trans::new(RBA::Vector::new(100, -100)) + inst = c0.insert(RBA::CellInstArray::new(c1.cell_index, t)) + + ti = RBA::ICplxTrans::new(2.5, 45.0, false, RBA::Vector::new(10, 20)) + t = RBA::Trans::new(1) + + assert_equal(inst.to_s, "cell_index=1 r0 100,-100") + + c0.transform_into(t) + assert_equal(inst.to_s, "cell_index=1 r0 100,100") + + c0.transform_into(ti) + assert_equal(inst.to_s, "cell_index=1 r0 0,354") + + c0.transform(t) + assert_equal(inst.to_s, "cell_index=1 r90 -354,0") + + c0.transform(ti) + assert_equal(inst.to_s, "cell_index=1 r135 *2.5 -616,-606") + + g = RBA::Layout::new + c0 = g.create_cell("c0") + c1 = g.create_cell("c1") + + t = RBA::Trans::new(RBA::Vector::new(100, -100)) + inst = c0.insert(RBA::CellInstArray::new(c1.cell_index, t)) + + ti = RBA::DCplxTrans::new(2.5, 45.0, false, RBA::DVector::new(0.01, 0.02)) + t = RBA::DTrans::new(1) + + assert_equal(inst.to_s, "cell_index=1 r0 100,-100") + + c0.transform_into(t) + assert_equal(inst.to_s, "cell_index=1 r0 100,100") + + c0.transform_into(ti) + assert_equal(inst.to_s, "cell_index=1 r0 0,354") + + c0.transform(t) + assert_equal(inst.to_s, "cell_index=1 r90 -354,0") + + c0.transform(ti) + assert_equal(inst.to_s, "cell_index=1 r135 *2.5 -616,-606") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 3fc2e64ce..c927e9dfb 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -68,9 +68,21 @@ class DBNetlist_TestClass < TestBase cc.dont_purge = true assert_equal(cc.dont_purge, true) + begin + nl.remove(cc) # not in netlist yet + assert_equal(false, true) + rescue + end + nl.add(cc) cc.name = "ABC" + begin + nl.add(cc) # already in netlist + assert_equal(false, true) + rescue + end + names = [] nl.each_circuit { |i| names << i.name } assert_equal(names, [ c.name, cc.name ]) @@ -119,9 +131,22 @@ class DBNetlist_TestClass < TestBase assert_equal(c.name, "XYZ") cc = RBA::GenericDeviceClass::new + + begin + nl.remove(cc) # not in netlist yet + assert_equal(false, true) + rescue + end + nl.add(cc) cc.name = "ABC" + begin + nl.add(cc) # already in netlist + assert_equal(false, true) + rescue + end + names = [] nl.each_device_class { |i| names << i.name } assert_equal(names, [ c.name, cc.name ]) diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index 7eb87c8e3..e95a62a07 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -645,8 +645,8 @@ END begin_circuit BUF BUF match_nets INT $10 match_nets IN IN -net_mismatch OUT OUT net_mismatch INT2 $11 +net_mismatch OUT OUT match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -715,8 +715,8 @@ END begin_circuit BUF BUF net_mismatch INT $10 match_nets IN IN -match_nets OUT OUT net_mismatch INT2 $11 +match_nets OUT OUT match_pins $0 $1 match_pins $1 $3 match_pins $2 $0