From cdf4d08fd377b9a6b32e354b52022a51730c3215 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 1 Jun 2020 23:28:04 +0200 Subject: [PATCH 01/26] WIP * Maybe fixed a performance issue on box-trees: the iterator wasn't going down to the very bottom of the tree on initialization * Added array quad skipping in display of shape arrays --- src/db/db/dbArray.h | 77 ++++++++++++++- src/db/db/dbBoxTree.h | 56 ++++++++--- src/db/db/dbInstances.cc | 5 +- src/db/db/dbInstances.h | 1 - src/db/db/dbShapeIterator.cc | 97 ++++++++++++++++++- src/db/db/dbShapes.h | 33 +++++++ src/db/unit_tests/dbBoxTreeTests.cc | 88 +++++++++++++++++ .../laybasic/layRedrawThreadWorker.cc | 49 ++++++++-- 8 files changed, 374 insertions(+), 32 deletions(-) diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index 8b55c6c35..7def9fe86 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; @@ -806,6 +810,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; @@ -1420,6 +1439,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; @@ -1835,6 +1884,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 */ diff --git a/src/db/db/dbBoxTree.h b/src/db/db/dbBoxTree.h index 0e3a8ae67..7217d2159 100644 --- a/src/db/db/dbBoxTree.h +++ b/src/db/db/dbBoxTree.h @@ -577,12 +577,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 +599,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 +673,7 @@ private: * whose box overlaps or touches a specified test box. */ -template +template class box_tree { public: @@ -1175,7 +1178,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 () < thin_aspect * bbox.height ()) { + // separate by height only + center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2); + } else if (bbox.height () < thin_aspect * bbox.width ()) { + // 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) { @@ -1578,12 +1590,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 +1612,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 +1653,7 @@ private: * is sorted. */ -template +template class unstable_box_tree { public: @@ -2103,7 +2118,16 @@ private: } obj_iterator qloc [5] = { from, from, from, from, from }; - point_type center (bbox.center ()); + point_type center; + if (bbox.width () < thin_aspect * bbox.height ()) { + // separate by height only + center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2); + } else if (bbox.height () < thin_aspect * bbox.width ()) { + // 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) { diff --git a/src/db/db/dbInstances.cc b/src/db/db/dbInstances.cc index 096d515de..488b86a92 100644 --- a/src/db/db/dbInstances.cc +++ b/src/db/db/dbInstances.cc @@ -640,13 +640,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/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/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/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index d48752965..87dedc4ce 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -1582,6 +1582,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 +1598,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 +1623,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; + } } From 9c4648a5b59019ad55230e48c8981f5a1f24b32e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 2 Jun 2020 23:59:09 +0200 Subject: [PATCH 02/26] WIP: some bug fixing and enhancements. --- src/db/db/dbBoxTree.h | 119 +++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/src/db/db/dbBoxTree.h b/src/db/db/dbBoxTree.h index 7217d2159..f34d49f01 100644 --- a/src/db/db/dbBoxTree.h +++ b/src/db/db/dbBoxTree.h @@ -168,19 +168,20 @@ 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 () @@ -195,7 +196,7 @@ public: 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); + box_tree_node *n = new box_tree_node (parent, m_center, m_corner, quad); for (unsigned int i = 0; i < 5; ++i) { n->m_lenq[i] = m_lenq[i]; } @@ -249,14 +250,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; + 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; + + 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; + } + } }; /** @@ -459,28 +498,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); } } @@ -1179,10 +1199,10 @@ private: // the bins are: overall, ur, ul, ll, lr, empty element_iterator qloc [6] = { from, from, from, from, from, from }; point_type center; - if (bbox.width () < thin_aspect * bbox.height ()) { + 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 () < thin_aspect * bbox.width ()) { + } else if (bbox.height () < bbox.width () / thin_aspect) { // separate by width only center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ()); } else { @@ -1236,7 +1256,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; } @@ -1472,28 +1492,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); } } @@ -2119,10 +2120,10 @@ private: obj_iterator qloc [5] = { from, from, from, from, from }; point_type center; - if (bbox.width () < thin_aspect * bbox.height ()) { + 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 () < thin_aspect * bbox.width ()) { + } else if (bbox.height () < bbox.width () / thin_aspect) { // separate by width only center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ()); } else { @@ -2182,7 +2183,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; } From 999c0652620b839cd1655eb9176a14df099ce899 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 4 Jun 2020 12:17:34 +0200 Subject: [PATCH 03/26] Introducing iterated arrays for instances Iterated instances are created for OASIS files using irregular repetitions in viewer mode. Reason: this way, the same drawing optimization than for iterated shape arrays can be applied. As this is a new API feature, some adjustments had to be made to incorporate them into the code. --- src/db/db/dbArray.h | 26 +++ src/db/db/dbHash.h | 6 + src/db/db/dbInstances.cc | 24 ++- src/db/db/dbLayoutDiff.cc | 4 + src/db/db/dbTextWriter.cc | 65 +++--- src/db/db/gsiDeclDbCell.cc | 103 +++------- src/db/unit_tests/dbLayoutDiffTests.cc | 4 + .../laybasic/layBrowseInstancesForm.cc | 10 +- src/laybasic/laybasic/layBrowseShapesForm.cc | 10 +- .../laybasic/layRedrawThreadWorker.cc | 42 +++- .../gds2/db_plugin/dbGDS2WriterBase.cc | 188 +++++++++--------- .../oasis/db_plugin/dbOASISReader.cc | 38 ++++ .../oasis/db_plugin/dbOASISWriter.cc | 28 ++- .../diff/lay_plugin/layDiffToolDialog.cc | 4 + src/tl/tl/tlUnitTest.cc | 1 + 15 files changed, 342 insertions(+), 211 deletions(-) diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index 7def9fe86..78e7fd402 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -1641,6 +1641,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 * 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/dbInstances.cc b/src/db/db/dbInstances.cc index 488b86a92..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 ()) { 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/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..d6f2bef9d 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,88 +371,12 @@ 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 < 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 == other; } static bool not_equal (const C *i, const C &other) @@ -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" 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/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/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index 87dedc4ce..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; } - } } @@ -1756,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) { @@ -1765,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/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/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/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/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); From adfd6532133feb15b0ecf286788f9d762b366f7b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 4 Jun 2020 13:01:11 +0200 Subject: [PATCH 04/26] WIP: refactoring - include fuzzy compare in array, don't put into GSI method impl. --- src/db/db/dbArray.h | 146 ++++++++++++++++++++++++++- src/db/db/gsiDeclDbCell.cc | 6 +- testdata/ruby/dbCellInstArrayTest.rb | 16 +-- 3 files changed, 156 insertions(+), 12 deletions(-) diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index 78e7fd402..a5e1222ca 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -115,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; @@ -558,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); @@ -566,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; @@ -706,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); @@ -718,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) { @@ -983,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); @@ -997,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) { @@ -1104,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); @@ -1116,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); @@ -1206,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); @@ -1219,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) { @@ -2019,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 */ @@ -2044,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 * diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index d6f2bef9d..a0ff3b0f6 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -371,17 +371,17 @@ struct cell_inst_array_defs static bool less (const C *i, const C &other) { - return *i < other; + return i->less (other); } static bool equal (const C *i, const C &other) { - return *i == other; + 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) 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) From 8b0ab115edd494383d787be52613cd6a55210d62 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 4 Jun 2020 15:38:53 +0200 Subject: [PATCH 05/26] Memory footprint optimization for quad tree --- src/db/db/dbBoxTree.h | 64 +++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/src/db/db/dbBoxTree.h b/src/db/db/dbBoxTree.h index f34d49f01..982993496 100644 --- a/src/db/db/dbBoxTree.h +++ b/src/db/db/dbBoxTree.h @@ -187,9 +187,9 @@ public: ~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; } } } @@ -197,12 +197,14 @@ public: 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, m_corner, quad); - for (unsigned int i = 0; i < 5; ++i) { - n->m_lenq[i] = m_lenq[i]; - } + 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; @@ -210,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 @@ -239,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); } } } @@ -268,8 +292,8 @@ public: private: box_tree_node *mp_parent; - size_t m_lenq [5]; - box_tree_node *mp_children [4]; + 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); @@ -285,15 +309,15 @@ private: m_center = center; m_corner = corner; - for (int i = 0; i < 5; ++i) { - m_lenq[i] = 0; - } + m_lenq = m_len = 0; for (int i = 0; i < 4; ++i) { - mp_children[i] = 0; + m_childrefs [i] = 0; } + mp_parent = (box_tree_node *)((char *) parent + quad); if (parent) { - parent->mp_children [quad] = this; + m_len = (parent->m_childrefs [quad] >> 1); + parent->m_childrefs [quad] = size_t (this); } } }; From 0a10635363ce835a9e83b3722d2b703e7e57c8a1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 13 Jun 2020 19:55:33 +0200 Subject: [PATCH 06/26] Fixed memory statistics for array (wasn't taking the right statistics for irregular arrays) --- src/db/db/dbArray.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index a5e1222ca..1a2de4246 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -2508,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); } } From b91e2324d0ed026595f7cfcebeadcd7f7c0fe2fa Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 26 Jun 2020 17:01:03 +0200 Subject: [PATCH 07/26] Netlist compare enhancement This enhancement targets towards a better resolution of ambiguities. The enhancement is to utilize knowledge about device and subcircuit equivalences to avoid stale branches of the ambiguity resolution tree. So far following these branches could lead to a contradictions which render an ambiguitiy resolution choice useless. One effect of this change is enhanced reproducibility of the matching log because some pointers are not involved anymore. --- src/db/db/dbNetlistCompare.cc | 1287 +++++++++++++++----- src/db/db/dbNetlistCompare.h | 27 +- src/db/unit_tests/dbNetlistCompareTests.cc | 278 ++++- testdata/lvs/double_height2.lvsdb | 2 +- testdata/lvs/ringo_simple_dmos.lvsdb.1 | 2 +- testdata/lvs/ringo_simple_dmos.lvsdb.2 | 912 -------------- 6 files changed, 1245 insertions(+), 1263 deletions(-) delete mode 100644 testdata/lvs/ringo_simple_dmos.lvsdb.2 diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 39be51719..7844e4f1b 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,20 @@ 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), 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; NetlistCompareLogger *logger; CircuitPinMapper *circuit_pin_mapper; + SubCircuitEquivalenceTracker *subcircuit_equivalence; + DeviceEquivalenceTracker *device_equivalence; + tl::RelativeProgress *progress; }; // -------------------------------------------------------------------------------------------------------------------- @@ -619,6 +681,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,9 +960,9 @@ 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; } }; @@ -1053,7 +1125,7 @@ public: * with a proposed identity. With "with_ambiguous", amiguities are resolved by trying * different combinations in tentative mode and deciding for one combination if possible. */ - 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, bool with_ambiguous, CompareData *data); private: std::vector m_nodes; @@ -1426,7 +1498,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 +1511,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 +1685,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 +1735,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 +1787,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 +1828,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 +1872,174 @@ NetGraph::build (const db::Circuit *c, DeviceCategorizer &device_categorizer, Ci } } +/** + * @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) +{ + 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 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; + } + + } + + 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, bool with_ambiguous, CompareData *data) { - size_t new_nodes = 0; + // NOTE: we can skip edges to known nodes because we did a pre-analysis making sure those are compatible - std::vector nodes; + std::vector > nodes; nodes.reserve (ee - e); - std::vector other_nodes; + std::vector > other_nodes; other_nodes.reserve (ee - e); - if (options ()->debug_netcompare) { - tl::info << indent(depth) << "considering transitions:"; - } - bool first = true; + 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, with_ambiguous, 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) { @@ -1714,16 +2071,6 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc size_t other_net_index = n->other_net_index (); NetGraphNode *n_other = & data->other->node (other_net_index); - if (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 +2088,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 @@ -1773,46 +2231,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,10 +2251,10 @@ 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; } }; @@ -1841,12 +2265,12 @@ static void sort_node_range_by_best_match (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; @@ -1896,7 +2320,7 @@ 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) +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) { std::string indent_s; if (options ()->debug_netcompare) { @@ -1913,49 +2337,80 @@ 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 ()) { + const NetGraphNode *n = nodes.front ().first; + const NetGraphNode *n_other = other_nodes.front ().first; + + // reject the transition if the edges provide a contradiction to already established equivalences + + if (! edges_are_compatible (*nodes.front ().second, *other_nodes.front ().second, *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 ()) { // 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 ()); + 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); + 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): " << nodes.front ()->net ()->expanded_name () << " vs. " << other_nodes.front ()->net ()->expanded_name (); + tl::info << indent_s << "deduced match (singular): " << n->net ()->expanded_name () << " vs. " << n_other->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 ()); + if (! tentative) { + ++*data->progress; + if (data->logger) { + 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 (n->net (), n_other->net ()); + } else { + data->logger->match_nets (n->net (), n_other->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; + if (data->depth_first || tentative) { + size_t bt_count = derive_node_identities (ni, depth + 1, n_branch, tentative, with_ambiguous, data); + if (bt_count == failed_match) { + if (tentative) { + return failed_match; + } + } else { + new_nodes += bt_count; + } } new_nodes += 1; - } else if (nodes.front ()->has_unknown_other ()) { + } else if (n->has_unknown_other ()) { // accept this solution as the pairing is possible - } else if (nodes.front ()->has_other ()) { + } else if (n->has_other ()) { // this decision leads to a contradiction - if (data->other->node_index_for_net (other_nodes.front ()->net ()) != nodes.front ()->other_net_index ()) { + if (data->other->node_index_for_net (n_other->net ()) != n->other_net_index ()) { return failed_match; } @@ -1974,38 +2429,38 @@ NetGraph::derive_node_identities_from_node_set (std::vector node_ranges; - 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; @@ -2038,9 +2493,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 +2503,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,14 +2523,21 @@ NetGraph::derive_node_identities_from_node_set (std::vectornum == 1) { - if (! (*nr->n1)->has_any_other () && ! (*nr->n2)->has_any_other ()) { + if (! edges_are_compatible (*nr->n1->second, *nr->n2->second, *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 (! nr->n1->first->has_any_other () && ! nr->n2->first->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 (tentative && ! data->dont_consider_net_names && net_names_are_different (nr->n1->first->net (), nr->n2->first->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 (); + tl::info << indent_s << "rejecting pair as names are not identical: " << nr->n1->first->net ()->expanded_name () << " vs. " << nr->n2->first->net ()->expanded_name (); } return failed_match; } @@ -2083,41 +2545,47 @@ NetGraph::derive_node_identities_from_node_set (std::vector 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 ()); + size_t ni = node_index_for_net (nr->n1->first->net ()); + size_t other_ni = data->other->node_index_for_net (nr->n2->first->net ()); - TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni); + 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): " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name (); + tl::info << indent_s << "deduced match (singular): " << nr->n1->first->net ()->expanded_name () << " vs. " << nr->n2->first->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 ()); + 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 (nr->n1->first->net (), nr->n2->first->net ()); + } else { + data->logger->match_nets (nr->n1->first->net (), nr->n2->first->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; + if (data->depth_first || tentative) { + size_t bt_count = derive_node_identities (ni, depth + 1, n_branch, tentative, with_ambiguous, data); + if (bt_count == failed_match) { + if (tentative) { + return failed_match; + } + } else { + new_nodes += bt_count; + } } - } else if ((*nr->n1)->has_unknown_other ()) { + new_nodes += 1; + + } else if (nr->n1->first->has_unknown_other ()) { // accept any other net - } else if ((*nr->n1)->has_other ()) { + } else if (nr->n1->first->has_other ()) { // this decision leads to a contradiction - if (data->other->node_index_for_net ((*nr->n2)->net ()) != (*nr->n1)->other_net_index ()) { + if (data->other->node_index_for_net (nr->n2->first->net ()) != nr->n1->first->other_net_index ()) { return failed_match; } @@ -2157,48 +2625,56 @@ NetGraph::derive_node_identities_from_node_set (std::vector::const_iterator> iters1, iters2; + std::vector >::const_iterator> iters1, iters2; - for (std::vector::const_iterator i1 = nr->n1; i1 != nr->nn1; ++i1) { - if (! (*i1)->has_any_other ()) { + 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)->net ()); + 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)->has_any_other ()) { + 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)->net ()); + 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) { + for (std::vector >::const_iterator>::const_iterator ii1 = iters1.begin (); ii1 != iters1.end (); ++ii1) { - std::vector::const_iterator i1 = *ii1; + std::vector >::const_iterator i1 = *ii1; bool any = false; - for (std::vector::const_iterator>::const_iterator ii2 = iters2.begin (); ii2 != iters2.end (); ++ii2) { + for (std::vector >::const_iterator>::const_iterator ii2 = iters2.begin (); ii2 != iters2.end (); ++ii2) { - std::vector::const_iterator i2 = *ii2; + std::vector >::const_iterator i2 = *ii2; - if (seen.find (*i2) != seen.end ()) { + if (seen.find (i2->first) != 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 (); + 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; + } + + 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, nr->num * n_branch, &tn, with_ambiguous, data); if (bt_count != failed_match) { @@ -2212,14 +2688,14 @@ NetGraph::derive_node_identities_from_node_set (std::vectorfirst, pairs.back ().second); } else { // identified a new pair new_nodes += bt_count + 1; - pairs.push_back (std::make_pair (*i1, *i2)); - seen.insert (*i2); + pairs.push_back (std::make_pair (i1->first, i2->first)); + seen.insert (i2->first); any = true; } @@ -2249,7 +2725,7 @@ NetGraph::derive_node_identities_from_node_set (std::vectorfirst->net ()); size_t other_ni = data->other->node_index_for_net (p->second->net ()); - TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni); + 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)) { @@ -2258,6 +2734,7 @@ NetGraph::derive_node_identities_from_node_set (std::vectorfirst->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name (); } } + if (data->logger) { bool ambiguous = equivalent_other_nodes.has_attribute (p->second); if (ambiguous) { @@ -2267,6 +2744,8 @@ NetGraph::derive_node_identities_from_node_set (std::vectorprogress; + } // And seek further from these pairs @@ -2287,7 +2766,7 @@ NetGraph::derive_node_identities_from_node_set (std::vectorfirst->net ()); size_t other_ni = data->other->node_index_for_net (p->second->net ()); - TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni); + TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni, dm, dm_other, *data->device_equivalence, scm, scm_other, *data->subcircuit_equivalence, depth); } @@ -2320,6 +2799,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 +3011,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 +3043,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 +3076,8 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const } + ++progress; + } if (mp_logger) { @@ -2663,8 +3148,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 +3218,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) @@ -2860,6 +3419,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 +3442,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,9 +3475,13 @@ 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.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); if (ni > 0 && ni != failed_match) { @@ -2934,29 +3501,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; @@ -2971,9 +3544,12 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, data.max_n_branch = m_max_n_branch; data.dont_consider_net_names = m_dont_consider_net_names; 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*/, pass > 0 /*with ambiguities*/, &data); if (ni > 0 && ni != failed_match) { new_identities += ni; if (options ()->debug_netcompare) { @@ -2982,6 +3558,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 +3573,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 +3592,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 +3610,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 +3826,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 +3835,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 +3880,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 +4041,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 +4055,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 +4085,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 +4361,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 +4379,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/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index c0d190f6d..718d85ffd 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -1147,8 +1147,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" @@ -1212,8 +1212,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" @@ -1274,9 +1301,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" @@ -2552,8 +2579,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 +2684,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" @@ -2794,6 +2872,67 @@ TEST(18_ClockTree) "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); } TEST(19_SymmetricCircuit) @@ -2922,24 +3061,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_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_ambiguous_nets q0 Q0\n" + "match_ambiguous_nets q1 Q1\n" + "match_nets $11 CS0\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_nets nn2 NN2\n" + "match_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" @@ -3805,11 +4043,11 @@ TEST(25c_JoinSymmetricNetsMultipleMessedUp) // 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" ) 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/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 deleted file mode 100644 index 45fc263a5..000000000 --- a/testdata/lvs/ringo_simple_dmos.lvsdb.2 +++ /dev/null @@ -1,912 +0,0 @@ -#%lvsdb-klayout - -# Layout -layout( - top(RINGO) - unit(0.001) - - # Layer section - # This section lists the mask layers (drawing or derived) and their connections. - - # Mask layers - layer(l4 '1/0') - layer(l5 '5/0') - layer(l10 '8/0') - layer(l13 '9/0') - layer(l14 '10/0') - layer(l15 '11/0') - layer(l9) - layer(l3) - layer(l1) - layer(l11) - layer(l8) - layer(l6) - layer(l12) - - # Mask layer connectivity - connect(l4 l4 l11) - connect(l5 l5 l10) - connect(l10 l5 l10 l13 l3 l1 l11 l8 l6 l12) - connect(l13 l10 l13 l14) - connect(l14 l13 l14 l15) - connect(l15 l14 l15) - connect(l9 l9) - connect(l3 l10 l3) - connect(l1 l10 l1) - connect(l11 l4 l10 l11) - connect(l8 l10 l8) - connect(l6 l10 l6) - connect(l12 l10 l12) - - # Global nets and connectivity - global(l9 SUBSTRATE) - global(l12 SUBSTRATE) - - # Device class section - class(PMOS MOS4) - class(NMOS MOS4) - - # Device abstracts section - # Device abstracts list the pin shapes of the devices. - device(D$PMOS PMOS - terminal(S - rect(l3 (125 -750) (450 1500)) - ) - terminal(G - rect(l5 (-125 -750) (250 1500)) - ) - terminal(D - rect(l1 (-550 -750) (425 1500)) - ) - terminal(B - rect(l4 (-125 -750) (250 1500)) - ) - ) - device(D$PMOS$1 PMOS - terminal(S - rect(l3 (-575 -750) (450 1500)) - ) - terminal(G - rect(l5 (-125 -750) (250 1500)) - ) - terminal(D - rect(l1 (125 -750) (425 1500)) - ) - terminal(B - rect(l4 (-125 -750) (250 1500)) - ) - ) - device(D$PMOS$2 PMOS - terminal(S - rect(l3 (-550 -750) (425 1500)) - ) - terminal(G - rect(l5 (-125 -750) (250 1500)) - ) - terminal(D - rect(l1 (125 -750) (425 1500)) - ) - terminal(B - rect(l4 (-125 -750) (250 1500)) - ) - ) - device(D$NMOS NMOS - terminal(S - rect(l8 (125 -475) (450 950)) - ) - terminal(G - rect(l5 (-125 -475) (250 950)) - ) - terminal(D - rect(l6 (-550 -475) (425 950)) - ) - terminal(B - rect(l9 (-125 -475) (250 950)) - ) - ) - device(D$NMOS$1 NMOS - terminal(S - rect(l8 (-575 -475) (450 950)) - ) - terminal(G - rect(l5 (-125 -475) (250 950)) - ) - terminal(D - rect(l6 (125 -475) (425 950)) - ) - terminal(B - rect(l9 (-125 -475) (250 950)) - ) - ) - device(D$NMOS$2 NMOS - terminal(S - rect(l8 (-550 -475) (425 950)) - ) - terminal(G - rect(l5 (-125 -475) (250 950)) - ) - terminal(D - rect(l6 (125 -475) (425 950)) - ) - terminal(B - rect(l9 (-125 -475) (250 950)) - ) - ) - - # Circuit section - # Circuits are the hierarchical building blocks of the netlist. - circuit(ND2X1 - - # Circuit boundary - rect((-100 400) (2600 7600)) - - # Nets with their geometries - net(1 name(VDD) - rect(l10 (1110 5160) (180 180)) - rect(l10 (-180 920) (180 180)) - rect(l10 (-180 -730) (180 180)) - rect(l13 (-240 -790) (300 1700)) - rect(l13 (-1350 0) (2400 800)) - rect(l13 (-1151 -401) (2 2)) - rect(l3 (-251 -2151) (425 1500)) - rect(l3 (-450 -1500) (425 1500)) - ) - net(2 name(OUT) - rect(l10 (1810 1770) (180 180)) - rect(l10 (-180 370) (180 180)) - rect(l10 (-1580 3760) (180 180)) - rect(l10 (-180 -730) (180 180)) - rect(l10 (-180 -730) (180 180)) - rect(l10 (1220 920) (180 180)) - rect(l10 (-180 -1280) (180 180)) - rect(l10 (-180 370) (180 180)) - polygon(l13 (-240 -4180) (0 1390) (490 0) (0 -300) (-190 0) (0 -1090)) - rect(l13 (-110 1390) (300 1400)) - polygon(l13 (-1890 0) (0 600) (300 0) (0 -300) (1590 0) (0 -300)) - rect(l13 (-141 -501) (2 2)) - rect(l13 (-1751 1099) (300 1400)) - rect(l13 (1100 -1700) (300 300)) - rect(l13 (-300 0) (300 1400)) - rect(l1 (-1750 -1450) (425 1500)) - rect(l1 (950 -1500) (425 1500)) - rect(l6 (-425 -4890) (425 950)) - ) - net(3 name(VSS) - rect(l10 (410 1770) (180 180)) - rect(l10 (-180 370) (180 180)) - rect(l13 (-240 -1300) (300 1360)) - rect(l13 (-650 -2160) (2400 800)) - rect(l13 (-1151 -401) (2 2)) - rect(l6 (-951 859) (425 950)) - ) - net(4 - rect(l8 (1000 1660) (425 950)) - rect(l8 (-450 -950) (425 950)) - ) - net(5 - rect(l4 (-100 4500) (2600 3500)) - ) - net(6 name(B) - rect(l5 (1425 2860) (250 1940)) - rect(l5 (-345 -950) (300 300)) - rect(l5 (-205 650) (250 2000)) - rect(l5 (-250 -2000) (250 2000)) - rect(l5 (-250 -5390) (250 1450)) - rect(l10 (-285 1050) (180 180)) - rect(l13 (-71 -91) (2 2)) - rect(l13 (-171 -151) (300 300)) - ) - net(7 name(A) - rect(l5 (725 2860) (250 1940)) - rect(l5 (-325 -1850) (300 300)) - rect(l5 (-225 1550) (250 2000)) - rect(l5 (-250 -2000) (250 2000)) - rect(l5 (-250 -5390) (250 1450)) - rect(l10 (-265 150) (180 180)) - rect(l13 (-91 -91) (2 2)) - rect(l13 (-151 -151) (300 300)) - ) - net(8 name(SUBSTRATE)) - - # Outgoing pins and their connections to nets - pin(1 name(VDD)) - pin(2 name(OUT)) - pin(3 name(VSS)) - pin(5) - pin(6 name(B)) - pin(7 name(A)) - pin(8 name(SUBSTRATE)) - - # Devices and their connections - device(1 D$PMOS - location(850 5800) - param(L 0.25) - param(W 1.5) - param(AS 0.3375) - param(AD 0.6375) - param(PS 1.95) - param(PD 3.85) - terminal(S 1) - terminal(G 7) - terminal(D 2) - terminal(B 5) - ) - device(2 D$PMOS$1 - location(1550 5800) - param(L 0.25) - param(W 1.5) - param(AS 0.3375) - param(AD 0.6375) - param(PS 1.95) - param(PD 3.85) - terminal(S 1) - terminal(G 6) - terminal(D 2) - terminal(B 5) - ) - device(3 D$NMOS - location(850 2135) - param(L 0.25) - param(W 0.95) - param(AS 0.21375) - param(AD 0.40375) - param(PS 1.4) - param(PD 2.75) - terminal(S 4) - terminal(G 7) - terminal(D 3) - terminal(B 8) - ) - device(4 D$NMOS$1 - location(1550 2135) - param(L 0.25) - param(W 0.95) - param(AS 0.21375) - param(AD 0.40375) - param(PS 1.4) - param(PD 2.75) - terminal(S 4) - terminal(G 6) - terminal(D 2) - terminal(B 8) - ) - - ) - circuit(INVX1 - - # Circuit boundary - rect((-100 400) (2000 7600)) - - # Nets with their geometries - net(1 name(VDD) - rect(l10 (410 6260) (180 180)) - rect(l10 (-180 -730) (180 180)) - rect(l10 (-180 -730) (180 180)) - rect(l13 (-240 -240) (300 1400)) - rect(l13 (-650 300) (1800 800)) - rect(l13 (-1450 -1100) (300 300)) - rect(l13 (299 399) (2 2)) - rect(l3 (-651 -2151) (425 1500)) - ) - net(2 name(OUT) - rect(l10 (1110 5160) (180 180)) - rect(l10 (-180 920) (180 180)) - rect(l10 (-180 -730) (180 180)) - rect(l10 (-180 -4120) (180 180)) - rect(l10 (-180 370) (180 180)) - rect(l13 (-240 -790) (300 4790)) - rect(l13 (-151 -2501) (2 2)) - rect(l1 (-226 1049) (425 1500)) - rect(l6 (-425 -4890) (425 950)) - ) - net(3 name(VSS) - rect(l10 (410 1770) (180 180)) - rect(l10 (-180 370) (180 180)) - rect(l13 (-240 -1300) (300 1360)) - rect(l13 (-650 -2160) (1800 800)) - rect(l13 (-851 -401) (2 2)) - rect(l8 (-651 859) (425 950)) - ) - net(4 - rect(l4 (-100 4500) (2000 3500)) - ) - net(5 name(IN) - rect(l5 (725 2860) (250 1940)) - rect(l5 (-525 -1850) (300 300)) - rect(l5 (-25 1550) (250 2000)) - rect(l5 (-250 -2000) (250 2000)) - rect(l5 (-250 -5390) (250 1450)) - rect(l10 (-465 150) (180 180)) - rect(l13 (-91 -91) (2 2)) - rect(l13 (-151 -151) (300 300)) - ) - net(6 name(SUBSTRATE)) - - # Outgoing pins and their connections to nets - pin(1 name(VDD)) - pin(2 name(OUT)) - pin(3 name(VSS)) - pin(4) - pin(5 name(IN)) - pin(6 name(SUBSTRATE)) - - # Devices and their connections - device(1 D$PMOS$2 - location(850 5800) - param(L 0.25) - param(W 1.5) - param(AS 0.6375) - param(AD 0.6375) - param(PS 3.85) - param(PD 3.85) - terminal(S 1) - terminal(G 5) - terminal(D 2) - terminal(B 4) - ) - device(2 D$NMOS$2 - location(850 2135) - param(L 0.25) - param(W 0.95) - param(AS 0.40375) - param(AD 0.40375) - param(PS 2.75) - param(PD 2.75) - terminal(S 3) - terminal(G 5) - terminal(D 2) - terminal(B 6) - ) - - ) - circuit(RINGO - - # Circuit boundary - rect((0 350) (25800 7650)) - - # Nets with their geometries - net(1 - rect(l13 (4040 2950) (610 300)) - ) - net(2 - rect(l13 (5550 2950) (900 300)) - ) - net(3 - rect(l13 (7350 2950) (900 300)) - ) - net(4 - rect(l13 (9150 2950) (900 300)) - ) - net(5 - rect(l13 (10950 2950) (900 300)) - ) - net(6 - rect(l13 (12750 2950) (900 300)) - ) - net(7 - rect(l13 (14550 2950) (900 300)) - ) - net(8 - rect(l13 (16350 2950) (900 300)) - ) - net(9 - rect(l13 (18150 2950) (900 300)) - ) - net(10 - rect(l13 (19950 2950) (900 300)) - ) - net(11 name(FB) - rect(l13 (21750 2950) (900 300)) - rect(l13 (-19530 590) (320 320)) - rect(l13 (17820 -320) (320 320)) - rect(l14 (-18400 -260) (200 200)) - rect(l14 (17940 -200) (200 200)) - rect(l15 (-18040 -300) (17740 400)) - rect(l15 (-17921 -201) (2 2)) - rect(l15 (-221 -201) (400 400)) - rect(l15 (17740 -400) (400 400)) - ) - net(12 name(VDD) - rect(l4 (500 4500) (1400 3500)) - rect(l4 (-1900 -3500) (600 3500)) - rect(l4 (23300 -3500) (1400 3500)) - rect(l4 (-100 -3500) (600 3500)) - rect(l10 (-24690 -1240) (180 180)) - rect(l10 (-180 370) (180 180)) - rect(l10 (-180 -1280) (180 180)) - rect(l10 (23220 370) (180 180)) - rect(l10 (-180 370) (180 180)) - rect(l10 (-180 -1280) (180 180)) - rect(l13 (-21741 859) (2 2)) - rect(l13 (-2351 -451) (1200 800)) - rect(l13 (-750 -1450) (300 1400)) - rect(l13 (-101 -351) (2 2)) - rect(l13 (-1251 -401) (600 800)) - rect(l13 (23400 -800) (1200 800)) - rect(l13 (-750 -1450) (300 1400)) - rect(l13 (-101 -351) (2 2)) - rect(l13 (549 -401) (600 800)) - rect(l11 (-24850 -1500) (500 1500)) - rect(l11 (22900 -1500) (500 1500)) - ) - net(13 name(OUT) - rect(l13 (23440 3840) (320 320)) - rect(l14 (-260 -260) (200 200)) - rect(l15 (-101 -101) (2 2)) - rect(l15 (-201 -201) (400 400)) - ) - net(14 name(ENABLE) - rect(l13 (2440 2940) (320 320)) - rect(l14 (-260 -260) (200 200)) - rect(l15 (-101 -101) (2 2)) - rect(l15 (-201 -201) (400 400)) - ) - net(15 name(VSS) - rect(l10 (1110 1610) (180 180)) - rect(l10 (-180 -1280) (180 180)) - rect(l10 (-180 370) (180 180)) - rect(l10 (23220 370) (180 180)) - rect(l10 (-180 -1280) (180 180)) - rect(l10 (-180 370) (180 180)) - rect(l13 (-21741 -391) (2 2)) - rect(l13 (-1901 -401) (300 1400)) - rect(l13 (-750 -1450) (1200 800)) - rect(l13 (-551 -401) (2 2)) - rect(l13 (-1251 -401) (600 800)) - rect(l13 (23850 -750) (300 1400)) - rect(l13 (-750 -1450) (1200 800)) - rect(l13 (-551 -401) (2 2)) - rect(l13 (549 -401) (600 800)) - rect(l12 (-24850 -800) (500 1500)) - rect(l12 (22900 -1500) (500 1500)) - ) - - # Outgoing pins and their connections to nets - pin(11 name(FB)) - pin(12 name(VDD)) - pin(13 name(OUT)) - pin(14 name(ENABLE)) - pin(15 name(VSS)) - - # Subcircuits and their connections - circuit(1 ND2X1 location(1800 0) - pin(0 12) - pin(1 1) - pin(2 15) - pin(3 12) - pin(4 11) - pin(5 14) - pin(6 15) - ) - circuit(2 INVX1 location(4200 0) - pin(0 12) - pin(1 2) - pin(2 15) - pin(3 12) - pin(4 1) - pin(5 15) - ) - circuit(3 INVX1 location(6000 0) - pin(0 12) - pin(1 3) - pin(2 15) - pin(3 12) - pin(4 2) - pin(5 15) - ) - circuit(4 INVX1 location(7800 0) - pin(0 12) - pin(1 4) - pin(2 15) - pin(3 12) - pin(4 3) - pin(5 15) - ) - circuit(5 INVX1 location(9600 0) - pin(0 12) - pin(1 5) - pin(2 15) - pin(3 12) - pin(4 4) - pin(5 15) - ) - circuit(6 INVX1 location(11400 0) - pin(0 12) - pin(1 6) - pin(2 15) - pin(3 12) - pin(4 5) - pin(5 15) - ) - circuit(7 INVX1 location(13200 0) - pin(0 12) - pin(1 7) - pin(2 15) - pin(3 12) - pin(4 6) - pin(5 15) - ) - circuit(8 INVX1 location(15000 0) - pin(0 12) - pin(1 8) - pin(2 15) - pin(3 12) - pin(4 7) - pin(5 15) - ) - circuit(9 INVX1 location(16800 0) - pin(0 12) - pin(1 9) - pin(2 15) - pin(3 12) - pin(4 8) - pin(5 15) - ) - circuit(10 INVX1 location(18600 0) - pin(0 12) - pin(1 10) - pin(2 15) - pin(3 12) - pin(4 9) - pin(5 15) - ) - circuit(11 INVX1 location(20400 0) - pin(0 12) - pin(1 11) - pin(2 15) - pin(3 12) - pin(4 10) - pin(5 15) - ) - circuit(12 INVX1 location(22200 0) - pin(0 12) - pin(1 13) - pin(2 15) - pin(3 12) - pin(4 11) - pin(5 15) - ) - - ) -) - -# Reference netlist -reference( - - # Device class section - class(PMOS MOS4) - class(NMOS MOS4) - - # Circuit section - # Circuits are the hierarchical building blocks of the netlist. - circuit(ND2X1 - - # Nets - net(1 name(VDD)) - net(2 name(OUT)) - net(3 name(VSS)) - net(4 name(NWELL)) - net(5 name(B)) - net(6 name(A)) - net(7 name(BULK)) - net(8 name('1')) - - # Outgoing pins and their connections to nets - pin(1 name(VDD)) - pin(2 name(OUT)) - pin(3 name(VSS)) - pin(4 name(NWELL)) - pin(5 name(B)) - pin(6 name(A)) - pin(7 name(BULK)) - - # Devices and their connections - device(1 PMOS - name($1) - param(L 0.25) - param(W 1.5) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 2) - terminal(G 6) - terminal(D 1) - terminal(B 4) - ) - device(2 PMOS - name($2) - param(L 0.25) - param(W 1.5) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 1) - terminal(G 5) - terminal(D 2) - terminal(B 4) - ) - device(3 NMOS - name($3) - param(L 0.25) - param(W 0.95) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 3) - terminal(G 6) - terminal(D 8) - terminal(B 7) - ) - device(4 NMOS - name($4) - param(L 0.25) - param(W 0.95) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 8) - terminal(G 5) - terminal(D 2) - terminal(B 7) - ) - - ) - circuit(INVX1 - - # Nets - net(1 name(VDD)) - net(2 name(OUT)) - net(3 name(VSS)) - net(4 name(NWELL)) - net(5 name(IN)) - net(6 name(BULK)) - - # Outgoing pins and their connections to nets - pin(1 name(VDD)) - pin(2 name(OUT)) - pin(3 name(VSS)) - pin(4 name(NWELL)) - pin(5 name(IN)) - pin(6 name(BULK)) - - # Devices and their connections - device(1 PMOS - name($1) - param(L 0.25) - param(W 1.5) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 1) - terminal(G 5) - terminal(D 2) - terminal(B 4) - ) - device(2 NMOS - name($2) - param(L 0.25) - param(W 0.95) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 3) - terminal(G 5) - terminal(D 2) - terminal(B 6) - ) - - ) - circuit(RINGO - - # Nets - net(1 name(VSS)) - net(2 name(VDD)) - net(3 name(FB)) - net(4 name(ENABLE)) - net(5 name(OUT)) - net(6 name('1')) - net(7 name('2')) - net(8 name('3')) - net(9 name('4')) - net(10 name('5')) - net(11 name('6')) - net(12 name('7')) - net(13 name('8')) - net(14 name('9')) - net(15 name('10')) - - # Outgoing pins and their connections to nets - pin(1 name(VSS)) - pin(2 name(VDD)) - pin(3 name(FB)) - pin(4 name(ENABLE)) - pin(5 name(OUT)) - - # Subcircuits and their connections - circuit(1 ND2X1 name($1) - pin(0 2) - pin(1 6) - pin(2 1) - pin(3 2) - pin(4 3) - pin(5 4) - pin(6 1) - ) - circuit(2 INVX1 name($2) - pin(0 2) - pin(1 7) - pin(2 1) - pin(3 2) - pin(4 6) - pin(5 1) - ) - circuit(3 INVX1 name($3) - pin(0 2) - pin(1 8) - pin(2 1) - pin(3 2) - pin(4 7) - pin(5 1) - ) - circuit(4 INVX1 name($4) - pin(0 2) - pin(1 9) - pin(2 1) - pin(3 2) - pin(4 8) - pin(5 1) - ) - circuit(5 INVX1 name($5) - pin(0 2) - pin(1 10) - pin(2 1) - pin(3 2) - pin(4 9) - pin(5 1) - ) - circuit(6 INVX1 name($6) - pin(0 2) - pin(1 11) - pin(2 1) - pin(3 2) - pin(4 10) - pin(5 1) - ) - circuit(7 INVX1 name($7) - pin(0 2) - pin(1 12) - pin(2 1) - pin(3 2) - pin(4 11) - pin(5 1) - ) - circuit(8 INVX1 name($8) - pin(0 2) - pin(1 13) - pin(2 1) - pin(3 2) - pin(4 12) - pin(5 1) - ) - circuit(9 INVX1 name($9) - pin(0 2) - pin(1 14) - pin(2 1) - pin(3 2) - pin(4 13) - pin(5 1) - ) - circuit(10 INVX1 name($10) - pin(0 2) - pin(1 15) - pin(2 1) - pin(3 2) - pin(4 14) - pin(5 1) - ) - circuit(11 INVX1 name($11) - pin(0 2) - pin(1 3) - pin(2 1) - pin(3 2) - pin(4 15) - pin(5 1) - ) - circuit(12 INVX1 name($12) - pin(0 2) - pin(1 5) - pin(2 1) - pin(3 2) - pin(4 3) - pin(5 1) - ) - - ) -) - -# Cross reference -xref( - circuit(INVX1 INVX1 match - xref( - net(4 4 match) - net(5 5 match) - net(2 2 match) - net(6 6 match) - net(1 1 match) - net(3 3 match) - pin(3 3 match) - pin(4 4 match) - pin(1 1 match) - pin(5 5 match) - pin(0 0 match) - pin(2 2 match) - device(2 2 match) - device(1 1 match) - ) - ) - circuit(ND2X1 ND2X1 nomatch - xref( - net(4 8 mismatch) - net(5 4 mismatch) - net(7 6 match) - net(6 5 match) - net(2 2 mismatch) - net(8 7 mismatch) - net(1 1 mismatch) - net(3 3 mismatch) - pin(3 3 match) - pin(5 5 match) - pin(4 4 match) - pin(1 1 match) - pin(6 6 match) - pin(0 0 match) - pin(2 2 match) - device(4 4 match) - device(3 3 mismatch) - device(2 2 match) - device(1 1 mismatch) - ) - ) - circuit(RINGO RINGO match - xref( - net(1 6 match) - net(10 15 match) - net(2 7 match) - net(3 8 match) - net(4 9 match) - net(5 10 match) - net(6 11 match) - net(7 12 match) - net(8 13 match) - net(9 14 match) - net(14 4 match) - net(11 3 match) - net(13 5 match) - net(12 2 match) - net(15 1 match) - pin(3 3 match) - pin(0 2 match) - pin(2 4 match) - pin(1 1 match) - pin(4 0 match) - circuit(2 2 match) - circuit(3 3 match) - circuit(4 4 match) - circuit(5 5 match) - circuit(6 6 match) - circuit(7 7 match) - circuit(8 8 match) - circuit(9 9 match) - circuit(10 10 match) - circuit(11 11 match) - circuit(12 12 match) - circuit(1 1 match) - ) - ) -) From 03dacbd2f51076864d168444b0b6258f275f7fa8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 26 Jun 2020 17:43:51 +0200 Subject: [PATCH 08/26] Updated testdata --- testdata/algo/lvs_test2_au.lvsdb.1 | 47 +- testdata/algo/lvs_test2_au.lvsdb.2 | 682 ----------------------------- testdata/ruby/dbNetlistCompare.rb | 4 +- 3 files changed, 21 insertions(+), 712 deletions(-) delete mode 100644 testdata/algo/lvs_test2_au.lvsdb.2 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 deleted file mode 100644 index 3719b24c1..000000000 --- a/testdata/algo/lvs_test2_au.lvsdb.2 +++ /dev/null @@ -1,682 +0,0 @@ -#%lvsdb-klayout - -# Layout -layout( - top(RINGO) - unit(0.001) - - # Layer section - # This section lists the mask layers (drawing or derived) and their connections. - - # Mask layers - layer(bulk '1/0') - layer(nwell '1/0') - layer(poly '3/0') - layer(poly_lbl '3/1') - layer(diff_cont '4/0') - layer(poly_cont '5/0') - layer(metal1 '6/0') - layer(metal1_lbl '6/1') - layer(via1 '7/0') - layer(metal2 '8/0') - layer(metal2_lbl '8/1') - layer(ntie) - layer(psd) - layer(ptie) - layer(nsd) - - # Mask layer connectivity - connect(nwell nwell ntie) - connect(poly poly poly_lbl poly_cont) - connect(poly_lbl poly) - connect(diff_cont diff_cont metal1 ntie psd ptie nsd) - connect(poly_cont poly poly_cont metal1) - connect(metal1 diff_cont poly_cont metal1 metal1_lbl via1) - connect(metal1_lbl metal1) - connect(via1 metal1 via1 metal2) - connect(metal2 via1 metal2 metal2_lbl) - connect(metal2_lbl metal2) - connect(ntie nwell diff_cont ntie) - connect(psd diff_cont psd) - connect(ptie diff_cont ptie) - connect(nsd diff_cont nsd) - - # Global nets and connectivity - global(bulk BULK) - global(ptie BULK) - - # Device class section - class(PMOS MOS4) - class(NMOS MOS4) - - # Device abstracts section - # Device abstracts list the pin shapes of the devices. - device(D$PMOS PMOS - terminal(S - rect(psd (-650 -875) (525 1750)) - ) - terminal(G - rect(poly (-125 -875) (250 1750)) - ) - terminal(D - rect(psd (125 -875) (550 1750)) - ) - terminal(B - rect(nwell (-125 -875) (250 1750)) - ) - ) - device(D$PMOS$1 PMOS - terminal(S - rect(psd (-675 -875) (550 1750)) - ) - terminal(G - rect(poly (-125 -875) (250 1750)) - ) - terminal(D - rect(psd (125 -875) (525 1750)) - ) - terminal(B - rect(nwell (-125 -875) (250 1750)) - ) - ) - device(D$NMOS NMOS - terminal(S - rect(nsd (-650 -875) (525 1750)) - ) - terminal(G - rect(poly (-125 -875) (250 1750)) - ) - terminal(D - rect(nsd (125 -875) (550 1750)) - ) - terminal(B - rect(bulk (-125 -875) (250 1750)) - ) - ) - device(D$NMOS$1 NMOS - terminal(S - rect(nsd (-675 -875) (550 1750)) - ) - terminal(G - rect(poly (-125 -875) (250 1750)) - ) - terminal(D - rect(nsd (125 -875) (525 1750)) - ) - terminal(B - rect(bulk (-125 -875) (250 1750)) - ) - ) - - # Circuit section - # Circuits are the hierarchical building blocks of the netlist. - circuit(INV2 - - # Circuit boundary - rect((-1700 -2440) (3100 7820)) - - # Nets with their geometries - net(1 - rect(nwell (-1400 1800) (2800 3580)) - rect(diff_cont (-1510 -650) (220 220)) - rect(ntie (-510 -450) (800 680)) - ) - net(2 name(IN) - rect(poly (-525 -250) (250 2500)) - rect(poly (-1425 -630) (2100 360)) - rect(poly (-125 -2230) (250 2500)) - rect(poly (-1050 -3850) (250 2400)) - rect(poly (550 1200) (250 2400)) - rect(poly (-250 -6000) (250 2400)) - rect(poly (-1050 1200) (250 2400)) - rect(poly_lbl (-526 -2601) (2 2)) - rect(poly_cont (-831 -111) (220 220)) - ) - net(3 name(OUT) - rect(diff_cont (-910 90) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(diff_cont (1380 3380) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(diff_cont (-220 -3820) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(diff_cont (-1820 3380) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(metal1 (1310 -3710) (360 2220)) - rect(metal1 (-1900 -800) (2220 360)) - rect(metal1 (-2280 -2400) (360 2840)) - rect(metal1 (-360 -3600) (360 1560)) - rect(metal1 (1240 2040) (360 1560)) - rect(metal1 (-360 -5160) (360 1560)) - rect(metal1 (-1960 2040) (360 1560)) - rect(metal1_lbl (1419 -2181) (2 2)) - rect(psd (-1851 524) (525 1750)) - rect(psd (1050 -1750) (525 1750)) - rect(nsd (-2100 -5350) (525 1750)) - rect(nsd (1050 -1750) (525 1750)) - ) - net(4 name(VSS) - rect(diff_cont (-110 90) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(diff_cont (-220 980) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(diff_cont (-220 -620) (220 220)) - rect(metal1 (-290 -290) (360 1560)) - rect(metal1 (-360 -1560) (360 1560)) - rect(via1 (-305 -705) (250 250)) - rect(via1 (-250 150) (250 250)) - rect(via1 (-250 -1450) (250 250)) - rect(via1 (-250 150) (250 250)) - rect(metal2 (-1525 -775) (2800 1700)) - rect(metal2_lbl (-161 -541) (2 2)) - rect(nsd (-1516 -1186) (550 1750)) - ) - net(5 name(VDD) - rect(diff_cont (-110 2490) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(diff_cont (-220 -1420) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(diff_cont (-220 180) (220 220)) - rect(metal1 (-290 -1490) (360 1560)) - rect(metal1 (-360 -1560) (360 1560)) - rect(via1 (-305 -1505) (250 250)) - rect(via1 (-250 150) (250 250)) - rect(via1 (-250 150) (250 250)) - rect(via1 (-250 150) (250 250)) - rect(metal2 (-1525 -1575) (2800 1700)) - rect(metal2_lbl (-151 -1251) (2 2)) - rect(psd (-1526 -476) (550 1750)) - ) - net(6 name(BULK) - rect(diff_cont (-110 -2160) (220 220)) - rect(ptie (-510 -450) (800 680)) - ) - - # Outgoing pins and their connections to nets - pin(1) - pin(2 name(IN)) - pin(3 name(OUT)) - pin(4 name(VSS)) - pin(5 name(VDD)) - pin(6 name(BULK)) - - # Devices and their connections - device(1 D$PMOS - device(D$PMOS$1 location(800 0)) - connect(0 S S) - connect(1 S D) - connect(0 G G) - connect(1 G G) - connect(0 D D) - connect(1 D S) - connect(0 B B) - connect(1 B B) - location(-400 3200) - param(L 0.25) - param(W 3.5) - param(AS 1.4) - param(AD 1.4) - param(PS 6.85) - param(PD 6.85) - terminal(S 3) - terminal(G 2) - terminal(D 5) - terminal(B 1) - ) - device(3 D$NMOS - device(D$NMOS$1 location(800 0)) - connect(0 S S) - connect(1 S D) - connect(0 G G) - connect(1 G G) - connect(0 D D) - connect(1 D S) - connect(0 B B) - connect(1 B B) - location(-400 -400) - param(L 0.25) - param(W 3.5) - param(AS 1.4) - param(AD 1.4) - param(PS 6.85) - param(PD 6.85) - terminal(S 3) - terminal(G 2) - terminal(D 4) - terminal(B 6) - ) - - ) - circuit(INV2PAIR - - # Circuit boundary - rect((0 -1640) (5740 7820)) - - # Nets with their geometries - net(1 name(BULK)) - net(2) - net(3) - net(4) - net(5) - net(6) - net(7) - - # Outgoing pins and their connections to nets - pin(1 name(BULK)) - pin(2) - pin(3) - pin(4) - pin(5) - pin(6) - pin(7) - - # Subcircuits and their connections - circuit(1 INV2 location(1700 800) - pin(0 7) - pin(1 5) - pin(2 4) - pin(3 3) - pin(4 2) - pin(5 1) - ) - circuit(2 INV2 location(4340 800) - pin(0 7) - pin(1 4) - pin(2 6) - pin(3 3) - pin(4 2) - pin(5 1) - ) - - ) - circuit(RINGO - - # Circuit boundary - rect((-1720 -2440) (26880 7820)) - - # Nets with their geometries - net(1 name(FB) - rect(metal1 (-1700 1620) (360 360)) - rect(via1 (-305 -305) (250 250)) - rect(via1 (23190 -250) (250 250)) - rect(metal2 (-23765 -325) (23840 400)) - rect(metal2_lbl (-22121 -201) (2 2)) - ) - net(2 name(OSC) - rect(via1 (24435 1675) (250 250)) - rect(metal2 (-325 -325) (400 400)) - rect(metal2_lbl (-201 -201) (2 2)) - ) - net(3 name(VDD) - rect(metal1 (-180 3900) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal2_lbl (-23941 -2221) (2 2)) - ) - net(4 name(VSS) - rect(metal1 (-180 -2220) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal1 (2280 -1120) (360 1120)) - rect(metal2_lbl (-23941 1099) (2 2)) - ) - net(5) - net(6) - net(7) - net(8) - net(9) - net(10) - net(11) - net(12) - - # Outgoing pins and their connections to nets - pin(1 name(FB)) - pin(2 name(OSC)) - pin(3 name(VDD)) - pin(4 name(VSS)) - - # Subcircuits and their connections - circuit(1 INV2PAIR location(19420 -800) - pin(0 4) - pin(1 3) - pin(2 4) - pin(3 1) - pin(4 10) - pin(5 2) - pin(6 3) - ) - circuit(2 INV2PAIR location(-1700 -800) - pin(0 4) - pin(1 3) - pin(2 4) - pin(3 8) - pin(4 1) - pin(5 9) - pin(6 3) - ) - circuit(3 INV2PAIR location(3580 -800) - pin(0 4) - pin(1 3) - pin(2 4) - pin(3 7) - pin(4 9) - pin(5 12) - pin(6 3) - ) - circuit(4 INV2PAIR location(8860 -800) - pin(0 4) - pin(1 3) - pin(2 4) - pin(3 6) - pin(4 12) - pin(5 11) - pin(6 3) - ) - circuit(5 INV2PAIR location(14140 -800) - pin(0 4) - pin(1 3) - pin(2 4) - pin(3 5) - pin(4 11) - pin(5 10) - pin(6 3) - ) - - ) -) - -# Reference netlist -reference( - - # Device class section - class(PMOS MOS4) - class(NMOS MOS4) - - # Circuit section - # Circuits are the hierarchical building blocks of the netlist. - circuit(INV2 - - # Nets - net(1 name('1')) - net(2 name('2')) - net(3 name('3')) - net(4 name('4')) - net(5 name('5')) - net(6 name('6')) - - # Outgoing pins and their connections to nets - pin(1 name('1')) - pin(2 name('2')) - pin(3 name('3')) - pin(4 name('4')) - pin(5 name('5')) - pin(6 name('6')) - - # Devices and their connections - device(1 PMOS - name($1) - param(L 0.25) - param(W 3.5) - param(AS 1.4) - param(AD 1.4) - param(PS 6.85) - param(PD 6.85) - terminal(S 3) - terminal(G 2) - terminal(D 5) - terminal(B 1) - ) - device(2 NMOS - name($3) - param(L 0.25) - param(W 3.5) - param(AS 1.4) - param(AD 1.4) - param(PS 6.85) - param(PD 6.85) - terminal(S 3) - terminal(G 2) - terminal(D 4) - terminal(B 6) - ) - - ) - circuit(INV2PAIR - - # Nets - net(1 name('1')) - net(2 name('2')) - net(3 name('3')) - net(4 name('4')) - net(5 name('5')) - net(6 name('6')) - net(7 name('7')) - - # Outgoing pins and their connections to nets - pin(1 name('1')) - pin(2 name('2')) - pin(3 name('3')) - pin(4 name('4')) - pin(5 name('5')) - pin(6 name('6')) - pin(7 name('7')) - - # Subcircuits and their connections - circuit(1 INV2 name($2) - pin(0 7) - pin(1 4) - pin(2 6) - pin(3 3) - pin(4 2) - pin(5 1) - ) - - ) - circuit(RINGO - - # Nets - net(1 name('1')) - net(2 name('2')) - net(3 name('3')) - net(4 name('4')) - net(5 name('6')) - net(6 name('5')) - net(7 name('101')) - net(8 name('8')) - net(9 name('102')) - net(10 name('7')) - net(11 name('103')) - - # Outgoing pins and their connections to nets - pin(1 name('1')) - pin(2 name('2')) - pin(3 name('3')) - pin(4 name('4')) - - # Subcircuits and their connections - circuit(1 INV2PAIR name($1) - pin(0 4) - pin(1 3) - pin(2 4) - pin(3 1) - pin(4 5) - pin(5 2) - pin(6 3) - ) - circuit(2 INV2PAIR name($2) - pin(0 4) - pin(1 3) - pin(2 4) - pin(3 1) - pin(4 1) - pin(5 6) - pin(6 3) - ) - circuit(3 INV2PAIR name($3) - pin(0 4) - pin(1 3) - pin(2 4) - pin(3 7) - pin(4 6) - pin(5 8) - pin(6 3) - ) - circuit(4 INV2PAIR name($4) - pin(0 4) - pin(1 3) - pin(2 4) - pin(3 9) - pin(4 8) - pin(5 10) - pin(6 3) - ) - circuit(5 INV2PAIR name($5) - pin(0 4) - pin(1 3) - pin(2 4) - pin(3 11) - pin(4 10) - pin(5 5) - pin(6 3) - ) - - ) - circuit(INV2PAIRX - - # Nets - net(1 name('1')) - net(2 name('2')) - net(3 name('3')) - net(4 name('4')) - net(5 name('5')) - net(6 name('6')) - net(7 name('7')) - - # Outgoing pins and their connections to nets - pin(1 name('1')) - pin(2 name('2')) - pin(3 name('3')) - pin(4 name('4')) - pin(5 name('5')) - pin(6 name('6')) - pin(7 name('7')) - - # Subcircuits and their connections - circuit(1 INV2 name($2) - pin(0 7) - pin(1 4) - pin(2 6) - pin(3 3) - pin(4 2) - pin(5 1) - ) - - ) -) - -# Cross reference -xref( - circuit(() INV2PAIRX mismatch - xref( - ) - ) - circuit(INV2 INV2 match - xref( - net(1 1 match) - net(6 6 match) - net(2 2 match) - net(3 3 match) - net(5 5 match) - net(4 4 match) - pin(0 0 match) - pin(5 5 match) - pin(1 1 match) - pin(2 2 match) - pin(4 4 match) - pin(3 3 match) - device(3 2 match) - device(1 1 match) - ) - ) - circuit(INV2PAIR INV2PAIR nomatch - xref( - net(2 2 mismatch) - net(3 3 mismatch) - net(5 4 match) - net(4 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(5 5 match) - pin(6 6 match) - pin(0 0 match) - circuit(2 () mismatch) - circuit(1 1 mismatch) - ) - ) - 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(2 2 match) - net(3 3 match) - net(4 4 match) - pin(() 0 match) - pin(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) - ) - ) -) 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 From 7a26768d8a758ce88f2b84ce6c21a982a2630428 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 26 Jun 2020 17:44:54 +0200 Subject: [PATCH 09/26] Updated testdata --- testdata/algo/lvs_test2b_au.lvsdb.1 | 47 ++++++++++++----------------- 1 file changed, 19 insertions(+), 28 deletions(-) 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) ) ) ) From 868adbceab59a71c2320bf72799ef5a63020b3e1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 26 Jun 2020 23:52:18 +0200 Subject: [PATCH 10/26] Updated golden test data --- testdata/lvs/double_height2_texts.lvsdb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 69fe4a5edf7cc63418b729d4fa383a96266ba0bd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Jun 2020 00:25:56 +0200 Subject: [PATCH 11/26] Provide a new option -o for unit test runner which sends the log to a file. This way we don't capture qWarning in the XML JUnit logs. --- src/unit_tests/unit_test_main.cc | 24 ++++++++++++++++++++++++ src/unit_tests/utTestConsole.cc | 30 ++++++++++++++++++++++-------- src/unit_tests/utTestConsole.h | 3 +++ 3 files changed, 49 insertions(+), 8 deletions(-) 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 (); }; } From c517aa4ff702b49aa15d70b5052b04d95510189e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Jun 2020 01:47:35 +0200 Subject: [PATCH 12/26] Cherry-picked MacOS fixes into master --- src/db/db/dbHierarchyBuilder.cc | 9 ++-- src/db/db/dbNetlistCompare.cc | 16 ++---- src/db/unit_tests/dbNetlistCompareTests.cc | 61 ++++++++++++++++++++++ src/gsi/gsi/gsiClassBase.cc | 34 +++++++++--- src/tl/tl/tlStream.cc | 27 +++++++--- src/tl/unit_tests/tlStreamTests.cc | 17 ++++++ 6 files changed, 137 insertions(+), 27 deletions(-) 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/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 7844e4f1b..ac592823d 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -3349,26 +3349,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: diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 718d85ffd..17b682925 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -2933,6 +2933,67 @@ TEST(18_ClockTree) "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); } TEST(19_SymmetricCircuit) 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/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index e6d8d2b6f..193b0fabe 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -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/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"); From 43ceeecf6e238c8df8363b64fcff46b3178ecb11 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Jun 2020 09:50:55 +0200 Subject: [PATCH 13/26] Golden test data for Ubuntu 20 and Windows, pipe output stream for Windows. --- src/tl/tl/tlStream.cc | 6 +- testdata/algo/lvs_test2_au.lvsdb.2 | 673 +++++++++++++++++ testdata/lvs/ringo_simple_dmos.lvsdb.2 | 912 +++++++++++++++++++++++ testdata/net_tracer/t6_all_nets.oas.gz.4 | Bin 0 -> 881 bytes 4 files changed, 1588 insertions(+), 3 deletions(-) create mode 100644 testdata/algo/lvs_test2_au.lvsdb.2 create mode 100644 testdata/lvs/ringo_simple_dmos.lvsdb.2 create mode 100644 testdata/net_tracer/t6_all_nets.oas.gz.4 diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index 193b0fabe..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); } } } diff --git a/testdata/algo/lvs_test2_au.lvsdb.2 b/testdata/algo/lvs_test2_au.lvsdb.2 new file mode 100644 index 000000000..a73de534d --- /dev/null +++ b/testdata/algo/lvs_test2_au.lvsdb.2 @@ -0,0 +1,673 @@ +#%lvsdb-klayout + +# Layout +layout( + top(RINGO) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(bulk '1/0') + layer(nwell '1/0') + layer(poly '3/0') + layer(poly_lbl '3/1') + layer(diff_cont '4/0') + layer(poly_cont '5/0') + layer(metal1 '6/0') + layer(metal1_lbl '6/1') + layer(via1 '7/0') + layer(metal2 '8/0') + layer(metal2_lbl '8/1') + layer(ntie) + layer(psd) + layer(ptie) + layer(nsd) + + # Mask layer connectivity + connect(nwell nwell ntie) + connect(poly poly poly_lbl poly_cont) + connect(poly_lbl poly) + connect(diff_cont diff_cont metal1 ntie psd ptie nsd) + connect(poly_cont poly poly_cont metal1) + connect(metal1 diff_cont poly_cont metal1 metal1_lbl via1) + connect(metal1_lbl metal1) + connect(via1 metal1 via1 metal2) + connect(metal2 via1 metal2 metal2_lbl) + connect(metal2_lbl metal2) + connect(ntie nwell diff_cont ntie) + connect(psd diff_cont psd) + connect(ptie diff_cont ptie) + connect(nsd diff_cont nsd) + + # Global nets and connectivity + global(bulk BULK) + global(ptie BULK) + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$PMOS PMOS + terminal(S + rect(psd (-650 -875) (525 1750)) + ) + terminal(G + rect(poly (-125 -875) (250 1750)) + ) + terminal(D + rect(psd (125 -875) (550 1750)) + ) + terminal(B + rect(nwell (-125 -875) (250 1750)) + ) + ) + device(D$PMOS$1 PMOS + terminal(S + rect(psd (-675 -875) (550 1750)) + ) + terminal(G + rect(poly (-125 -875) (250 1750)) + ) + terminal(D + rect(psd (125 -875) (525 1750)) + ) + terminal(B + rect(nwell (-125 -875) (250 1750)) + ) + ) + device(D$NMOS NMOS + terminal(S + rect(nsd (-650 -875) (525 1750)) + ) + terminal(G + rect(poly (-125 -875) (250 1750)) + ) + terminal(D + rect(nsd (125 -875) (550 1750)) + ) + terminal(B + rect(bulk (-125 -875) (250 1750)) + ) + ) + device(D$NMOS$1 NMOS + terminal(S + rect(nsd (-675 -875) (550 1750)) + ) + terminal(G + rect(poly (-125 -875) (250 1750)) + ) + terminal(D + rect(nsd (125 -875) (525 1750)) + ) + terminal(B + rect(bulk (-125 -875) (250 1750)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(INV2 + + # Circuit boundary + rect((-1700 -2440) (3100 7820)) + + # Nets with their geometries + net(1 + rect(nwell (-1400 1800) (2800 3580)) + rect(diff_cont (-1510 -650) (220 220)) + rect(ntie (-510 -450) (800 680)) + ) + net(2 name(IN) + rect(poly (-525 -250) (250 2500)) + rect(poly (-1425 -630) (2100 360)) + rect(poly (-125 -2230) (250 2500)) + rect(poly (-1050 -3850) (250 2400)) + rect(poly (550 1200) (250 2400)) + rect(poly (-250 -6000) (250 2400)) + rect(poly (-1050 1200) (250 2400)) + rect(poly_lbl (-526 -2601) (2 2)) + rect(poly_cont (-831 -111) (220 220)) + ) + net(3 name(OUT) + rect(diff_cont (-910 90) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(diff_cont (1380 3380) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(diff_cont (-220 -3820) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(diff_cont (-1820 3380) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(metal1 (1310 -3710) (360 2220)) + rect(metal1 (-1900 -800) (2220 360)) + rect(metal1 (-2280 -2400) (360 2840)) + rect(metal1 (-360 -3600) (360 1560)) + rect(metal1 (1240 2040) (360 1560)) + rect(metal1 (-360 -5160) (360 1560)) + rect(metal1 (-1960 2040) (360 1560)) + rect(metal1_lbl (1419 -2181) (2 2)) + rect(psd (-1851 524) (525 1750)) + rect(psd (1050 -1750) (525 1750)) + rect(nsd (-2100 -5350) (525 1750)) + rect(nsd (1050 -1750) (525 1750)) + ) + net(4 name(VSS) + rect(diff_cont (-110 90) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(diff_cont (-220 980) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(diff_cont (-220 -620) (220 220)) + rect(metal1 (-290 -290) (360 1560)) + rect(metal1 (-360 -1560) (360 1560)) + rect(via1 (-305 -705) (250 250)) + rect(via1 (-250 150) (250 250)) + rect(via1 (-250 -1450) (250 250)) + rect(via1 (-250 150) (250 250)) + rect(metal2 (-1525 -775) (2800 1700)) + rect(metal2_lbl (-161 -541) (2 2)) + rect(nsd (-1516 -1186) (550 1750)) + ) + net(5 name(VDD) + rect(diff_cont (-110 2490) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(diff_cont (-220 -1420) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(diff_cont (-220 180) (220 220)) + rect(metal1 (-290 -1490) (360 1560)) + rect(metal1 (-360 -1560) (360 1560)) + rect(via1 (-305 -1505) (250 250)) + rect(via1 (-250 150) (250 250)) + rect(via1 (-250 150) (250 250)) + rect(via1 (-250 150) (250 250)) + rect(metal2 (-1525 -1575) (2800 1700)) + rect(metal2_lbl (-151 -1251) (2 2)) + rect(psd (-1526 -476) (550 1750)) + ) + net(6 name(BULK) + rect(diff_cont (-110 -2160) (220 220)) + rect(ptie (-510 -450) (800 680)) + ) + + # Outgoing pins and their connections to nets + pin(1) + pin(2 name(IN)) + pin(3 name(OUT)) + pin(4 name(VSS)) + pin(5 name(VDD)) + pin(6 name(BULK)) + + # Devices and their connections + device(1 D$PMOS + device(D$PMOS$1 location(800 0)) + connect(0 S S) + connect(1 S D) + connect(0 G G) + connect(1 G G) + connect(0 D D) + connect(1 D S) + connect(0 B B) + connect(1 B B) + location(-400 3200) + param(L 0.25) + param(W 3.5) + param(AS 1.4) + param(AD 1.4) + param(PS 6.85) + param(PD 6.85) + terminal(S 3) + terminal(G 2) + terminal(D 5) + terminal(B 1) + ) + device(3 D$NMOS + device(D$NMOS$1 location(800 0)) + connect(0 S S) + connect(1 S D) + connect(0 G G) + connect(1 G G) + connect(0 D D) + connect(1 D S) + connect(0 B B) + connect(1 B B) + location(-400 -400) + param(L 0.25) + param(W 3.5) + param(AS 1.4) + param(AD 1.4) + param(PS 6.85) + param(PD 6.85) + terminal(S 3) + terminal(G 2) + terminal(D 4) + terminal(B 6) + ) + + ) + circuit(INV2PAIR + + # Circuit boundary + rect((0 -1640) (5740 7820)) + + # Nets with their geometries + net(1 name(BULK)) + net(2) + net(3) + net(4) + net(5) + net(6) + net(7) + + # Outgoing pins and their connections to nets + pin(1 name(BULK)) + pin(2) + pin(3) + pin(4) + pin(5) + pin(6) + pin(7) + + # Subcircuits and their connections + circuit(1 INV2 location(1700 800) + pin(0 7) + pin(1 5) + pin(2 4) + pin(3 3) + pin(4 2) + pin(5 1) + ) + circuit(2 INV2 location(4340 800) + pin(0 7) + pin(1 4) + pin(2 6) + pin(3 3) + pin(4 2) + pin(5 1) + ) + + ) + circuit(RINGO + + # Circuit boundary + rect((-1720 -2440) (26880 7820)) + + # Nets with their geometries + net(1 name(FB) + rect(metal1 (-1700 1620) (360 360)) + rect(via1 (-305 -305) (250 250)) + rect(via1 (23190 -250) (250 250)) + rect(metal2 (-23765 -325) (23840 400)) + rect(metal2_lbl (-22121 -201) (2 2)) + ) + net(2 name(OSC) + rect(via1 (24435 1675) (250 250)) + rect(metal2 (-325 -325) (400 400)) + rect(metal2_lbl (-201 -201) (2 2)) + ) + net(3 name(VDD) + rect(metal1 (-180 3900) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal2_lbl (-23941 -2221) (2 2)) + ) + net(4 name(VSS) + rect(metal1 (-180 -2220) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal1 (2280 -1120) (360 1120)) + rect(metal2_lbl (-23941 1099) (2 2)) + ) + net(5) + net(6) + net(7) + net(8) + net(9) + net(10) + net(11) + net(12) + + # Outgoing pins and their connections to nets + pin(1 name(FB)) + pin(2 name(OSC)) + pin(3 name(VDD)) + pin(4 name(VSS)) + + # Subcircuits and their connections + circuit(1 INV2PAIR location(19420 -800) + pin(0 4) + pin(1 3) + pin(2 4) + pin(3 1) + pin(4 10) + pin(5 2) + pin(6 3) + ) + circuit(2 INV2PAIR location(-1700 -800) + pin(0 4) + pin(1 3) + pin(2 4) + pin(3 8) + pin(4 1) + pin(5 9) + pin(6 3) + ) + circuit(3 INV2PAIR location(3580 -800) + pin(0 4) + pin(1 3) + pin(2 4) + pin(3 7) + pin(4 9) + pin(5 12) + pin(6 3) + ) + circuit(4 INV2PAIR location(8860 -800) + pin(0 4) + pin(1 3) + pin(2 4) + pin(3 6) + pin(4 12) + pin(5 11) + pin(6 3) + ) + circuit(5 INV2PAIR location(14140 -800) + pin(0 4) + pin(1 3) + pin(2 4) + pin(3 5) + pin(4 11) + pin(5 10) + pin(6 3) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(INV2 + + # Nets + net(1 name('1')) + net(2 name('2')) + net(3 name('3')) + net(4 name('4')) + net(5 name('5')) + net(6 name('6')) + + # Outgoing pins and their connections to nets + pin(1 name('1')) + pin(2 name('2')) + pin(3 name('3')) + pin(4 name('4')) + pin(5 name('5')) + pin(6 name('6')) + + # Devices and their connections + device(1 PMOS + name($1) + param(L 0.25) + param(W 3.5) + param(AS 1.4) + param(AD 1.4) + param(PS 6.85) + param(PD 6.85) + terminal(S 3) + terminal(G 2) + terminal(D 5) + terminal(B 1) + ) + device(2 NMOS + name($3) + param(L 0.25) + param(W 3.5) + param(AS 1.4) + param(AD 1.4) + param(PS 6.85) + param(PD 6.85) + terminal(S 3) + terminal(G 2) + terminal(D 4) + terminal(B 6) + ) + + ) + circuit(INV2PAIR + + # Nets + net(1 name('1')) + net(2 name('2')) + net(3 name('3')) + net(4 name('4')) + net(5 name('5')) + net(6 name('6')) + net(7 name('7')) + + # Outgoing pins and their connections to nets + pin(1 name('1')) + pin(2 name('2')) + pin(3 name('3')) + pin(4 name('4')) + pin(5 name('5')) + pin(6 name('6')) + pin(7 name('7')) + + # Subcircuits and their connections + circuit(1 INV2 name($2) + pin(0 7) + pin(1 4) + pin(2 6) + pin(3 3) + pin(4 2) + pin(5 1) + ) + + ) + circuit(RINGO + + # Nets + net(1 name('1')) + net(2 name('2')) + net(3 name('3')) + net(4 name('4')) + net(5 name('6')) + net(6 name('5')) + net(7 name('101')) + net(8 name('8')) + net(9 name('102')) + net(10 name('7')) + net(11 name('103')) + + # Outgoing pins and their connections to nets + pin(1 name('1')) + pin(2 name('2')) + pin(3 name('3')) + pin(4 name('4')) + + # Subcircuits and their connections + circuit(1 INV2PAIR name($1) + pin(0 4) + pin(1 3) + pin(2 4) + pin(3 1) + pin(4 5) + pin(5 2) + pin(6 3) + ) + circuit(2 INV2PAIR name($2) + pin(0 4) + pin(1 3) + pin(2 4) + pin(3 1) + pin(4 1) + pin(5 6) + pin(6 3) + ) + circuit(3 INV2PAIR name($3) + pin(0 4) + pin(1 3) + pin(2 4) + pin(3 7) + pin(4 6) + pin(5 8) + pin(6 3) + ) + circuit(4 INV2PAIR name($4) + pin(0 4) + pin(1 3) + pin(2 4) + pin(3 9) + pin(4 8) + pin(5 10) + pin(6 3) + ) + circuit(5 INV2PAIR name($5) + pin(0 4) + pin(1 3) + pin(2 4) + pin(3 11) + pin(4 10) + pin(5 5) + pin(6 3) + ) + + ) + circuit(INV2PAIRX + + # Nets + net(1 name('1')) + net(2 name('2')) + net(3 name('3')) + net(4 name('4')) + net(5 name('5')) + net(6 name('6')) + net(7 name('7')) + + # Outgoing pins and their connections to nets + pin(1 name('1')) + pin(2 name('2')) + pin(3 name('3')) + pin(4 name('4')) + pin(5 name('5')) + pin(6 name('6')) + pin(7 name('7')) + + # Subcircuits and their connections + circuit(1 INV2 name($2) + pin(0 7) + pin(1 4) + pin(2 6) + pin(3 3) + pin(4 2) + pin(5 1) + ) + + ) +) + +# Cross reference +xref( + circuit(() INV2PAIRX mismatch + xref( + ) + ) + circuit(INV2 INV2 match + xref( + net(1 1 match) + net(6 6 match) + net(2 2 match) + net(3 3 match) + net(5 5 match) + net(4 4 match) + pin(0 0 match) + pin(5 5 match) + pin(1 1 match) + pin(2 2 match) + pin(4 4 match) + pin(3 3 match) + device(3 2 match) + device(1 1 match) + ) + ) + circuit(INV2PAIR INV2PAIR nomatch + xref( + net(2 2 mismatch) + net(3 3 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(3 3 match) + pin(4 4 match) + pin(5 5 match) + pin(6 6 match) + pin(0 0 match) + circuit(1 () mismatch) + circuit(2 1 match) + ) + ) + circuit(RINGO RINGO nomatch + xref( + 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 0 match) + pin(1 1 match) + pin(2 2 match) + pin(3 3 match) + circuit(() 2 mismatch) + circuit(2 () mismatch) + circuit(1 1 match) + circuit(3 3 match) + circuit(4 4 match) + circuit(5 5 match) + ) + ) +) diff --git a/testdata/lvs/ringo_simple_dmos.lvsdb.2 b/testdata/lvs/ringo_simple_dmos.lvsdb.2 new file mode 100644 index 000000000..bd0320264 --- /dev/null +++ b/testdata/lvs/ringo_simple_dmos.lvsdb.2 @@ -0,0 +1,912 @@ +#%lvsdb-klayout + +# Layout +layout( + top(RINGO) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l4 '1/0') + layer(l5 '5/0') + layer(l10 '8/0') + layer(l13 '9/0') + layer(l14 '10/0') + layer(l15 '11/0') + layer(l9) + layer(l3) + layer(l1) + layer(l11) + layer(l8) + layer(l6) + layer(l12) + + # Mask layer connectivity + connect(l4 l4 l11) + connect(l5 l5 l10) + connect(l10 l5 l10 l13 l3 l1 l11 l8 l6 l12) + connect(l13 l10 l13 l14) + connect(l14 l13 l14 l15) + connect(l15 l14 l15) + connect(l9 l9) + connect(l3 l10 l3) + connect(l1 l10 l1) + connect(l11 l4 l10 l11) + connect(l8 l10 l8) + connect(l6 l10 l6) + connect(l12 l10 l12) + + # Global nets and connectivity + global(l9 SUBSTRATE) + global(l12 SUBSTRATE) + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$PMOS PMOS + terminal(S + rect(l3 (125 -750) (450 1500)) + ) + terminal(G + rect(l5 (-125 -750) (250 1500)) + ) + terminal(D + rect(l1 (-550 -750) (425 1500)) + ) + terminal(B + rect(l4 (-125 -750) (250 1500)) + ) + ) + device(D$PMOS$1 PMOS + terminal(S + rect(l3 (-575 -750) (450 1500)) + ) + terminal(G + rect(l5 (-125 -750) (250 1500)) + ) + terminal(D + rect(l1 (125 -750) (425 1500)) + ) + terminal(B + rect(l4 (-125 -750) (250 1500)) + ) + ) + device(D$PMOS$2 PMOS + terminal(S + rect(l3 (-550 -750) (425 1500)) + ) + terminal(G + rect(l5 (-125 -750) (250 1500)) + ) + terminal(D + rect(l1 (125 -750) (425 1500)) + ) + terminal(B + rect(l4 (-125 -750) (250 1500)) + ) + ) + device(D$NMOS NMOS + terminal(S + rect(l8 (125 -475) (450 950)) + ) + terminal(G + rect(l5 (-125 -475) (250 950)) + ) + terminal(D + rect(l6 (-550 -475) (425 950)) + ) + terminal(B + rect(l9 (-125 -475) (250 950)) + ) + ) + device(D$NMOS$1 NMOS + terminal(S + rect(l8 (-575 -475) (450 950)) + ) + terminal(G + rect(l5 (-125 -475) (250 950)) + ) + terminal(D + rect(l6 (125 -475) (425 950)) + ) + terminal(B + rect(l9 (-125 -475) (250 950)) + ) + ) + device(D$NMOS$2 NMOS + terminal(S + rect(l8 (-550 -475) (425 950)) + ) + terminal(G + rect(l5 (-125 -475) (250 950)) + ) + terminal(D + rect(l6 (125 -475) (425 950)) + ) + terminal(B + rect(l9 (-125 -475) (250 950)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(ND2X1 + + # Circuit boundary + rect((-100 400) (2600 7600)) + + # Nets with their geometries + net(1 name(VDD) + rect(l10 (1110 5160) (180 180)) + rect(l10 (-180 920) (180 180)) + rect(l10 (-180 -730) (180 180)) + rect(l13 (-240 -790) (300 1700)) + rect(l13 (-1350 0) (2400 800)) + rect(l13 (-1151 -401) (2 2)) + rect(l3 (-251 -2151) (425 1500)) + rect(l3 (-450 -1500) (425 1500)) + ) + net(2 name(OUT) + rect(l10 (1810 1770) (180 180)) + rect(l10 (-180 370) (180 180)) + rect(l10 (-1580 3760) (180 180)) + rect(l10 (-180 -730) (180 180)) + rect(l10 (-180 -730) (180 180)) + rect(l10 (1220 920) (180 180)) + rect(l10 (-180 -1280) (180 180)) + rect(l10 (-180 370) (180 180)) + polygon(l13 (-240 -4180) (0 1390) (490 0) (0 -300) (-190 0) (0 -1090)) + rect(l13 (-110 1390) (300 1400)) + polygon(l13 (-1890 0) (0 600) (300 0) (0 -300) (1590 0) (0 -300)) + rect(l13 (-141 -501) (2 2)) + rect(l13 (-1751 1099) (300 1400)) + rect(l13 (1100 -1700) (300 300)) + rect(l13 (-300 0) (300 1400)) + rect(l1 (-1750 -1450) (425 1500)) + rect(l1 (950 -1500) (425 1500)) + rect(l6 (-425 -4890) (425 950)) + ) + net(3 name(VSS) + rect(l10 (410 1770) (180 180)) + rect(l10 (-180 370) (180 180)) + rect(l13 (-240 -1300) (300 1360)) + rect(l13 (-650 -2160) (2400 800)) + rect(l13 (-1151 -401) (2 2)) + rect(l6 (-951 859) (425 950)) + ) + net(4 + rect(l8 (1000 1660) (425 950)) + rect(l8 (-450 -950) (425 950)) + ) + net(5 + rect(l4 (-100 4500) (2600 3500)) + ) + net(6 name(B) + rect(l5 (1425 2860) (250 1940)) + rect(l5 (-345 -950) (300 300)) + rect(l5 (-205 650) (250 2000)) + rect(l5 (-250 -2000) (250 2000)) + rect(l5 (-250 -5390) (250 1450)) + rect(l10 (-285 1050) (180 180)) + rect(l13 (-71 -91) (2 2)) + rect(l13 (-171 -151) (300 300)) + ) + net(7 name(A) + rect(l5 (725 2860) (250 1940)) + rect(l5 (-325 -1850) (300 300)) + rect(l5 (-225 1550) (250 2000)) + rect(l5 (-250 -2000) (250 2000)) + rect(l5 (-250 -5390) (250 1450)) + rect(l10 (-265 150) (180 180)) + rect(l13 (-91 -91) (2 2)) + rect(l13 (-151 -151) (300 300)) + ) + net(8 name(SUBSTRATE)) + + # Outgoing pins and their connections to nets + pin(1 name(VDD)) + pin(2 name(OUT)) + pin(3 name(VSS)) + pin(5) + pin(6 name(B)) + pin(7 name(A)) + pin(8 name(SUBSTRATE)) + + # Devices and their connections + device(1 D$PMOS + location(850 5800) + param(L 0.25) + param(W 1.5) + param(AS 0.3375) + param(AD 0.6375) + param(PS 1.95) + param(PD 3.85) + terminal(S 1) + terminal(G 7) + terminal(D 2) + terminal(B 5) + ) + device(2 D$PMOS$1 + location(1550 5800) + param(L 0.25) + param(W 1.5) + param(AS 0.3375) + param(AD 0.6375) + param(PS 1.95) + param(PD 3.85) + terminal(S 1) + terminal(G 6) + terminal(D 2) + terminal(B 5) + ) + device(3 D$NMOS + location(850 2135) + param(L 0.25) + param(W 0.95) + param(AS 0.21375) + param(AD 0.40375) + param(PS 1.4) + param(PD 2.75) + terminal(S 4) + terminal(G 7) + terminal(D 3) + terminal(B 8) + ) + device(4 D$NMOS$1 + location(1550 2135) + param(L 0.25) + param(W 0.95) + param(AS 0.21375) + param(AD 0.40375) + param(PS 1.4) + param(PD 2.75) + terminal(S 4) + terminal(G 6) + terminal(D 2) + terminal(B 8) + ) + + ) + circuit(INVX1 + + # Circuit boundary + rect((-100 400) (2000 7600)) + + # Nets with their geometries + net(1 name(VDD) + rect(l10 (410 6260) (180 180)) + rect(l10 (-180 -730) (180 180)) + rect(l10 (-180 -730) (180 180)) + rect(l13 (-240 -240) (300 1400)) + rect(l13 (-650 300) (1800 800)) + rect(l13 (-1450 -1100) (300 300)) + rect(l13 (299 399) (2 2)) + rect(l3 (-651 -2151) (425 1500)) + ) + net(2 name(OUT) + rect(l10 (1110 5160) (180 180)) + rect(l10 (-180 920) (180 180)) + rect(l10 (-180 -730) (180 180)) + rect(l10 (-180 -4120) (180 180)) + rect(l10 (-180 370) (180 180)) + rect(l13 (-240 -790) (300 4790)) + rect(l13 (-151 -2501) (2 2)) + rect(l1 (-226 1049) (425 1500)) + rect(l6 (-425 -4890) (425 950)) + ) + net(3 name(VSS) + rect(l10 (410 1770) (180 180)) + rect(l10 (-180 370) (180 180)) + rect(l13 (-240 -1300) (300 1360)) + rect(l13 (-650 -2160) (1800 800)) + rect(l13 (-851 -401) (2 2)) + rect(l8 (-651 859) (425 950)) + ) + net(4 + rect(l4 (-100 4500) (2000 3500)) + ) + net(5 name(IN) + rect(l5 (725 2860) (250 1940)) + rect(l5 (-525 -1850) (300 300)) + rect(l5 (-25 1550) (250 2000)) + rect(l5 (-250 -2000) (250 2000)) + rect(l5 (-250 -5390) (250 1450)) + rect(l10 (-465 150) (180 180)) + rect(l13 (-91 -91) (2 2)) + rect(l13 (-151 -151) (300 300)) + ) + net(6 name(SUBSTRATE)) + + # Outgoing pins and their connections to nets + pin(1 name(VDD)) + pin(2 name(OUT)) + pin(3 name(VSS)) + pin(4) + pin(5 name(IN)) + pin(6 name(SUBSTRATE)) + + # Devices and their connections + device(1 D$PMOS$2 + location(850 5800) + param(L 0.25) + param(W 1.5) + param(AS 0.6375) + param(AD 0.6375) + param(PS 3.85) + param(PD 3.85) + terminal(S 1) + terminal(G 5) + terminal(D 2) + terminal(B 4) + ) + device(2 D$NMOS$2 + location(850 2135) + param(L 0.25) + param(W 0.95) + param(AS 0.40375) + param(AD 0.40375) + param(PS 2.75) + param(PD 2.75) + terminal(S 3) + terminal(G 5) + terminal(D 2) + terminal(B 6) + ) + + ) + circuit(RINGO + + # Circuit boundary + rect((0 350) (25800 7650)) + + # Nets with their geometries + net(1 + rect(l13 (4040 2950) (610 300)) + ) + net(2 + rect(l13 (5550 2950) (900 300)) + ) + net(3 + rect(l13 (7350 2950) (900 300)) + ) + net(4 + rect(l13 (9150 2950) (900 300)) + ) + net(5 + rect(l13 (10950 2950) (900 300)) + ) + net(6 + rect(l13 (12750 2950) (900 300)) + ) + net(7 + rect(l13 (14550 2950) (900 300)) + ) + net(8 + rect(l13 (16350 2950) (900 300)) + ) + net(9 + rect(l13 (18150 2950) (900 300)) + ) + net(10 + rect(l13 (19950 2950) (900 300)) + ) + net(11 name(FB) + rect(l13 (21750 2950) (900 300)) + rect(l13 (-19530 590) (320 320)) + rect(l13 (17820 -320) (320 320)) + rect(l14 (-18400 -260) (200 200)) + rect(l14 (17940 -200) (200 200)) + rect(l15 (-18040 -300) (17740 400)) + rect(l15 (-17921 -201) (2 2)) + rect(l15 (-221 -201) (400 400)) + rect(l15 (17740 -400) (400 400)) + ) + net(12 name(VDD) + rect(l4 (500 4500) (1400 3500)) + rect(l4 (-1900 -3500) (600 3500)) + rect(l4 (23300 -3500) (1400 3500)) + rect(l4 (-100 -3500) (600 3500)) + rect(l10 (-24690 -1240) (180 180)) + rect(l10 (-180 370) (180 180)) + rect(l10 (-180 -1280) (180 180)) + rect(l10 (23220 370) (180 180)) + rect(l10 (-180 370) (180 180)) + rect(l10 (-180 -1280) (180 180)) + rect(l13 (-21741 859) (2 2)) + rect(l13 (-2351 -451) (1200 800)) + rect(l13 (-750 -1450) (300 1400)) + rect(l13 (-101 -351) (2 2)) + rect(l13 (-1251 -401) (600 800)) + rect(l13 (23400 -800) (1200 800)) + rect(l13 (-750 -1450) (300 1400)) + rect(l13 (-101 -351) (2 2)) + rect(l13 (549 -401) (600 800)) + rect(l11 (-24850 -1500) (500 1500)) + rect(l11 (22900 -1500) (500 1500)) + ) + net(13 name(OUT) + rect(l13 (23440 3840) (320 320)) + rect(l14 (-260 -260) (200 200)) + rect(l15 (-101 -101) (2 2)) + rect(l15 (-201 -201) (400 400)) + ) + net(14 name(ENABLE) + rect(l13 (2440 2940) (320 320)) + rect(l14 (-260 -260) (200 200)) + rect(l15 (-101 -101) (2 2)) + rect(l15 (-201 -201) (400 400)) + ) + net(15 name(VSS) + rect(l10 (1110 1610) (180 180)) + rect(l10 (-180 -1280) (180 180)) + rect(l10 (-180 370) (180 180)) + rect(l10 (23220 370) (180 180)) + rect(l10 (-180 -1280) (180 180)) + rect(l10 (-180 370) (180 180)) + rect(l13 (-21741 -391) (2 2)) + rect(l13 (-1901 -401) (300 1400)) + rect(l13 (-750 -1450) (1200 800)) + rect(l13 (-551 -401) (2 2)) + rect(l13 (-1251 -401) (600 800)) + rect(l13 (23850 -750) (300 1400)) + rect(l13 (-750 -1450) (1200 800)) + rect(l13 (-551 -401) (2 2)) + rect(l13 (549 -401) (600 800)) + rect(l12 (-24850 -800) (500 1500)) + rect(l12 (22900 -1500) (500 1500)) + ) + + # Outgoing pins and their connections to nets + pin(11 name(FB)) + pin(12 name(VDD)) + pin(13 name(OUT)) + pin(14 name(ENABLE)) + pin(15 name(VSS)) + + # Subcircuits and their connections + circuit(1 ND2X1 location(1800 0) + pin(0 12) + pin(1 1) + pin(2 15) + pin(3 12) + pin(4 11) + pin(5 14) + pin(6 15) + ) + circuit(2 INVX1 location(4200 0) + pin(0 12) + pin(1 2) + pin(2 15) + pin(3 12) + pin(4 1) + pin(5 15) + ) + circuit(3 INVX1 location(6000 0) + pin(0 12) + pin(1 3) + pin(2 15) + pin(3 12) + pin(4 2) + pin(5 15) + ) + circuit(4 INVX1 location(7800 0) + pin(0 12) + pin(1 4) + pin(2 15) + pin(3 12) + pin(4 3) + pin(5 15) + ) + circuit(5 INVX1 location(9600 0) + pin(0 12) + pin(1 5) + pin(2 15) + pin(3 12) + pin(4 4) + pin(5 15) + ) + circuit(6 INVX1 location(11400 0) + pin(0 12) + pin(1 6) + pin(2 15) + pin(3 12) + pin(4 5) + pin(5 15) + ) + circuit(7 INVX1 location(13200 0) + pin(0 12) + pin(1 7) + pin(2 15) + pin(3 12) + pin(4 6) + pin(5 15) + ) + circuit(8 INVX1 location(15000 0) + pin(0 12) + pin(1 8) + pin(2 15) + pin(3 12) + pin(4 7) + pin(5 15) + ) + circuit(9 INVX1 location(16800 0) + pin(0 12) + pin(1 9) + pin(2 15) + pin(3 12) + pin(4 8) + pin(5 15) + ) + circuit(10 INVX1 location(18600 0) + pin(0 12) + pin(1 10) + pin(2 15) + pin(3 12) + pin(4 9) + pin(5 15) + ) + circuit(11 INVX1 location(20400 0) + pin(0 12) + pin(1 11) + pin(2 15) + pin(3 12) + pin(4 10) + pin(5 15) + ) + circuit(12 INVX1 location(22200 0) + pin(0 12) + pin(1 13) + pin(2 15) + pin(3 12) + pin(4 11) + pin(5 15) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(ND2X1 + + # Nets + net(1 name(VDD)) + net(2 name(OUT)) + net(3 name(VSS)) + net(4 name(NWELL)) + net(5 name(B)) + net(6 name(A)) + net(7 name(BULK)) + net(8 name('1')) + + # Outgoing pins and their connections to nets + pin(1 name(VDD)) + pin(2 name(OUT)) + pin(3 name(VSS)) + pin(4 name(NWELL)) + pin(5 name(B)) + pin(6 name(A)) + pin(7 name(BULK)) + + # Devices and their connections + device(1 PMOS + name($1) + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 2) + terminal(G 6) + terminal(D 1) + terminal(B 4) + ) + device(2 PMOS + name($2) + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 1) + terminal(G 5) + terminal(D 2) + terminal(B 4) + ) + device(3 NMOS + name($3) + param(L 0.25) + param(W 0.95) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 3) + terminal(G 6) + terminal(D 8) + terminal(B 7) + ) + device(4 NMOS + name($4) + param(L 0.25) + param(W 0.95) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 8) + terminal(G 5) + terminal(D 2) + terminal(B 7) + ) + + ) + circuit(INVX1 + + # Nets + net(1 name(VDD)) + net(2 name(OUT)) + net(3 name(VSS)) + net(4 name(NWELL)) + net(5 name(IN)) + net(6 name(BULK)) + + # Outgoing pins and their connections to nets + pin(1 name(VDD)) + pin(2 name(OUT)) + pin(3 name(VSS)) + pin(4 name(NWELL)) + pin(5 name(IN)) + pin(6 name(BULK)) + + # Devices and their connections + device(1 PMOS + name($1) + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 1) + terminal(G 5) + terminal(D 2) + terminal(B 4) + ) + device(2 NMOS + name($2) + param(L 0.25) + param(W 0.95) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 3) + terminal(G 5) + terminal(D 2) + terminal(B 6) + ) + + ) + circuit(RINGO + + # Nets + net(1 name(VSS)) + net(2 name(VDD)) + net(3 name(FB)) + net(4 name(ENABLE)) + net(5 name(OUT)) + net(6 name('1')) + net(7 name('2')) + net(8 name('3')) + net(9 name('4')) + net(10 name('5')) + net(11 name('6')) + net(12 name('7')) + net(13 name('8')) + net(14 name('9')) + net(15 name('10')) + + # Outgoing pins and their connections to nets + pin(1 name(VSS)) + pin(2 name(VDD)) + pin(3 name(FB)) + pin(4 name(ENABLE)) + pin(5 name(OUT)) + + # Subcircuits and their connections + circuit(1 ND2X1 name($1) + pin(0 2) + pin(1 6) + pin(2 1) + pin(3 2) + pin(4 3) + pin(5 4) + pin(6 1) + ) + circuit(2 INVX1 name($2) + pin(0 2) + pin(1 7) + pin(2 1) + pin(3 2) + pin(4 6) + pin(5 1) + ) + circuit(3 INVX1 name($3) + pin(0 2) + pin(1 8) + pin(2 1) + pin(3 2) + pin(4 7) + pin(5 1) + ) + circuit(4 INVX1 name($4) + pin(0 2) + pin(1 9) + pin(2 1) + pin(3 2) + pin(4 8) + pin(5 1) + ) + circuit(5 INVX1 name($5) + pin(0 2) + pin(1 10) + pin(2 1) + pin(3 2) + pin(4 9) + pin(5 1) + ) + circuit(6 INVX1 name($6) + pin(0 2) + pin(1 11) + pin(2 1) + pin(3 2) + pin(4 10) + pin(5 1) + ) + circuit(7 INVX1 name($7) + pin(0 2) + pin(1 12) + pin(2 1) + pin(3 2) + pin(4 11) + pin(5 1) + ) + circuit(8 INVX1 name($8) + pin(0 2) + pin(1 13) + pin(2 1) + pin(3 2) + pin(4 12) + pin(5 1) + ) + circuit(9 INVX1 name($9) + pin(0 2) + pin(1 14) + pin(2 1) + pin(3 2) + pin(4 13) + pin(5 1) + ) + circuit(10 INVX1 name($10) + pin(0 2) + pin(1 15) + pin(2 1) + pin(3 2) + pin(4 14) + pin(5 1) + ) + circuit(11 INVX1 name($11) + pin(0 2) + pin(1 3) + pin(2 1) + pin(3 2) + pin(4 15) + pin(5 1) + ) + circuit(12 INVX1 name($12) + pin(0 2) + pin(1 5) + pin(2 1) + pin(3 2) + pin(4 3) + pin(5 1) + ) + + ) +) + +# Cross reference +xref( + circuit(INVX1 INVX1 match + xref( + net(4 4 match) + net(5 5 match) + net(2 2 match) + net(6 6 match) + net(1 1 match) + net(3 3 match) + pin(3 3 match) + pin(4 4 match) + pin(1 1 match) + pin(5 5 match) + pin(0 0 match) + pin(2 2 match) + device(2 2 match) + device(1 1 match) + ) + ) + circuit(ND2X1 ND2X1 nomatch + xref( + net(4 8 mismatch) + net(5 4 mismatch) + net(7 6 match) + net(6 5 match) + net(2 2 mismatch) + net(8 7 mismatch) + net(1 1 mismatch) + net(3 3 mismatch) + pin(3 3 match) + pin(5 5 match) + pin(4 4 match) + pin(1 1 match) + pin(6 6 match) + pin(0 0 match) + pin(2 2 match) + device(3 3 mismatch) + device(4 4 match) + device(2 2 match) + device(1 1 mismatch) + ) + ) + circuit(RINGO RINGO match + xref( + net(1 6 match) + net(10 15 match) + net(2 7 match) + net(3 8 match) + net(4 9 match) + net(5 10 match) + net(6 11 match) + net(7 12 match) + net(8 13 match) + net(9 14 match) + net(14 4 match) + net(11 3 match) + net(13 5 match) + net(12 2 match) + net(15 1 match) + pin(3 3 match) + pin(0 2 match) + pin(2 4 match) + pin(1 1 match) + pin(4 0 match) + circuit(2 2 match) + circuit(3 3 match) + circuit(4 4 match) + circuit(5 5 match) + circuit(6 6 match) + circuit(7 7 match) + circuit(8 8 match) + circuit(9 9 match) + circuit(10 10 match) + circuit(11 11 match) + circuit(12 12 match) + circuit(1 1 match) + ) + ) +) 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 0000000000000000000000000000000000000000..f5aba5bdaf06e934551083078e47f5ded690c90e GIT binary patch literal 881 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>u=u%#1T#ZOMn`5wCuTOFNW6+6GpDm> zkaMVKNW3G036r(pW>PU`e!(gkiri#o zn#>_CQt^TD5?dqJLN<}g7wk=pn^@;^uH+Y~n8tXAv6J%&D=&-S7uLIsZ#V@DPO$yp z61mO9u!!ja^Bqxh9_=ZK~ z#y-ZiOhBnh)+RQn9MGm|jEk5KGEEWTW#VNM{KB$CXrX{$!3WWuydu@WP@Tr8(IWkl zN5sbHgVb4GZU!y^US`1`EE~)^`M8<6KJ)M$;5o>~bf1Syux5i&uK-Z2f%iK<$3ixi zer6_-YF37ncdV6LjYSHM@uvic6r^loy(2GD3$oSb0@qTYfg%DTHX0kO{|a)ma~)>m z*v!UukrQOzLmmx;IXWj8*9vm8f$RgyN&qD;@bof+t*|j#qJ38YC`NM4<692p(V-JisRShY{pYZWgY&%uGlQ7OVh!j0NfvnBSNhMlxVv005GkDail; literal 0 HcmV?d00001 From cc31b191881d788f4aad734fb1b3cd3bc64af057 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 27 Jun 2020 20:41:52 +0200 Subject: [PATCH 14/26] Another golden testdata update for Windows. --- testdata/algo/lvs_test2b_au.lvsdb.2 | 47 ++++++++++++----------------- 1 file changed, 19 insertions(+), 28 deletions(-) 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) ) ) ) From b413cb9d74ee146c63beadde0908878e2d41b161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Mon, 29 Jun 2020 20:47:57 +0200 Subject: [PATCH 15/26] Netlist compare: Ambiguity resolution through name matching now default (can be turned off) (#594) * WIP: some refactoring * WIP: some refactoring * Netlist compare: introducing ambiguity resolution by net names By default now net names are used for resolving ambiguities. If net names match, they will be used to associate nets if the choice is ambiguous. This is usually much faster and more reliable than trying to resolve ambiguities through topology analysis. This feature can be disabled using "consider_net_names(false)" in the LVS script. * Some refactoring, Jenkinsfile modified for better test coverage --- Jenkinsfile | 1 + src/db/db/dbNetlistCompare.cc | 655 +++++++++++---------- src/db/db/gsiDeclDbNetlistCompare.cc | 12 + src/db/unit_tests/dbNetlistCompareTests.cc | 286 ++++++++- src/lay/lay/doc/about/lvs_ref_global.xml | 9 + src/lay/lay/doc/about/lvs_ref_netter.xml | 12 + src/lvs/lvs/built-in-macros/_lvs_engine.rb | 8 +- src/lvs/lvs/built-in-macros/_lvs_netter.rb | 14 + testdata/lvs/double_height.lvsdb | 4 +- testdata/lvs/double_height2.lvs | 2 + testdata/lvs/double_height2_texts.lvs | 2 + testdata/lvs/floating.lvs | 2 + testdata/lvs/invchain_cheat.lvs | 2 + testdata/lvs/ringo_simple_blackboxing.lvs | 2 + 14 files changed, 672 insertions(+), 339 deletions(-) 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/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index ac592823d..c9103b7c7 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -616,7 +616,7 @@ class NetGraph; struct CompareData { CompareData () - : other (0), max_depth (0), max_n_branch (0), depth_first (true), dont_consider_net_names (false), logger (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) { } @@ -625,6 +625,7 @@ struct CompareData 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; @@ -967,6 +968,9 @@ struct CompareNodePtr }; class TentativeNodeMapping; +struct NodeRange; +class DeviceMapperForTargetNode; +class SubCircuitMapperForTargetNode; std::string indent (size_t depth) { @@ -1112,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; @@ -1133,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); }; // -------------------------------------------------------------------------------------------------------------------- @@ -1920,7 +1922,7 @@ static bool edges_are_compatible (const NetGraphNode::edge_type &e, const NetGra } 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) +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 @@ -2029,7 +2031,7 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr // 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); + 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) { @@ -2064,7 +2066,7 @@ 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); @@ -2221,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."; @@ -2260,7 +2262,7 @@ namespace { } -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 ()); @@ -2319,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); @@ -2329,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) { @@ -2353,81 +2654,15 @@ NetGraph::derive_node_identities_from_node_set (std::vectordevice_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 ()) { - - // 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, because that is the only candidate - data->logger->net_mismatch (n->net (), n_other->net ()); - } else { - data->logger->match_nets (n->net (), n_other->net ()); - } - } - } - - if (data->depth_first || tentative) { - size_t bt_count = derive_node_identities (ni, depth + 1, n_branch, tentative, with_ambiguous, data); - if (bt_count == failed_match) { - if (tentative) { - return failed_match; - } - } else { - new_nodes += bt_count; - } - } - - new_nodes += 1; - - } else if (n->has_unknown_other ()) { - - // accept this solution as the pairing is possible - - } 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 { - - // 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 (); @@ -2469,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; } @@ -2484,7 +2719,7 @@ NetGraph::derive_node_identities_from_node_set (std::vectorwith_ambiguous) { std::stable_sort (node_ranges.begin (), node_ranges.end ()); } @@ -2523,79 +2758,13 @@ NetGraph::derive_node_identities_from_node_set (std::vectornum == 1) { - if (! edges_are_compatible (*nr->n1->second, *nr->n2->second, *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 (! nr->n1->first->has_any_other () && ! nr->n2->first->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->first->net (), nr->n2->first->net ())) { - if (options ()->debug_netcompare) { - tl::info << indent_s << "rejecting pair as names are not identical: " << nr->n1->first->net ()->expanded_name () << " vs. " << nr->n2->first->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->first->net ()); - size_t other_ni = data->other->node_index_for_net (nr->n2->first->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): " << nr->n1->first->net ()->expanded_name () << " vs. " << nr->n2->first->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 (nr->n1->first->net (), nr->n2->first->net ()); - } else { - data->logger->match_nets (nr->n1->first->net (), nr->n2->first->net ()); - } - } - } - - if (data->depth_first || tentative) { - size_t bt_count = derive_node_identities (ni, depth + 1, n_branch, tentative, with_ambiguous, data); - if (bt_count == failed_match) { - if (tentative) { - return failed_match; - } - } else { - new_nodes += bt_count; - } - } - - new_nodes += 1; - - } else if (nr->n1->first->has_unknown_other ()) { - - // accept any other net - - } else if (nr->n1->first->has_other ()) { - - // this decision leads to a contradiction - if (data->other->node_index_for_net (nr->n2->first->net ()) != nr->n1->first->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) { @@ -2609,168 +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->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; - - for (std::vector >::const_iterator>::const_iterator ii2 = iters2.begin (); ii2 != iters2.end (); ++ii2) { - - std::vector >::const_iterator i2 = *ii2; - - if (seen.find (i2->first) != seen.end ()) { - continue; - } - - // 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; - } - - 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, 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->first, pairs.back ().second); - - } else { - - // identified a new pair - new_nodes += bt_count + 1; - pairs.push_back (std::make_pair (i1->first, i2->first)); - seen.insert (i2->first); - 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 (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, 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, dm, dm_other, *data->device_equivalence, scm, scm_other, *data->subcircuit_equivalence, depth); - - } - - } + new_nodes += n; if (options ()->debug_netcompare) { tl::info << indent_s << "finished analysis of ambiguity group with " << nr->num << " members"; @@ -3471,13 +3484,14 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, 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) { @@ -3537,13 +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 tentatively*/, 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) { 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/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 17b682925..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"); @@ -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"); @@ -1293,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); @@ -1345,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); @@ -1386,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); @@ -1428,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); @@ -1470,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); @@ -1511,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); @@ -1558,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); @@ -1620,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); @@ -1788,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" @@ -1845,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); @@ -1912,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); @@ -1979,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); @@ -2053,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); @@ -2204,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); @@ -2283,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); @@ -2358,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); @@ -2440,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); @@ -2813,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); @@ -2822,6 +2851,68 @@ 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" @@ -3103,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); @@ -3122,8 +3214,8 @@ TEST(19_SymmetricCircuit) "match_nets g1 G1\n" "match_nets $44 YI\n" "match_nets $14 WELL\n" - "match_nets nn2 NN2\n" - "match_nets nn2_ NN2_\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" @@ -3221,8 +3313,8 @@ TEST(19_SymmetricCircuit) "match_nets g1 G1\n" "match_nets $44 YI\n" "match_nets $14 WELL\n" - "match_nets nn2 NN2\n" - "match_nets nn2_ NN2_\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" @@ -3359,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); @@ -3503,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) @@ -3539,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); @@ -3711,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); @@ -3831,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); @@ -3930,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); @@ -4021,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 (), @@ -4059,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 (); @@ -4098,6 +4344,7 @@ 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 (); @@ -4137,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" @@ -4181,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 (), @@ -4213,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")); @@ -4235,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")); @@ -4264,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/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/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/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_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/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 From 4bd26721346075137c2a166457c5547f5d319ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Fri, 3 Jul 2020 23:40:55 +0200 Subject: [PATCH 16/26] Fixed #592 (layer mapping issue) (#601) --- src/db/db/dbStreamLayers.cc | 2 +- src/db/unit_tests/dbStreamLayerTests.cc | 38 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) 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/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); +} From dcd0476efcc76010d9c87aa31d0c3407476344a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Fri, 3 Jul 2020 23:41:09 +0200 Subject: [PATCH 17/26] Implemented issue #598 (Cell#transform) (#600) --- src/db/db/dbCell.h | 24 ++++++++++ src/db/db/gsiDeclDbCell.cc | 62 ++++++++++++++++++++++++++ src/db/unit_tests/dbCellTests.cc | 75 ++++++++++++++++++++++++++++++++ testdata/ruby/dbLayoutTest.rb | 53 ++++++++++++++++++++++ 4 files changed, 214 insertions(+) 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/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index a0ff3b0f6..e41ef5972 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1563,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 (); @@ -2347,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/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/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") From 4db20b3b483c264d9c4a988922292b7c47319219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Fri, 3 Jul 2020 23:41:20 +0200 Subject: [PATCH 18/26] Fixed #596 (crash on library _destroy) (#597) --- src/db/db/dbLibrary.h | 1 + src/db/db/gsiDeclDbLibrary.cc | 11 +++++++++++ 2 files changed, 12 insertions(+) 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/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" ) + From 6365ddfe828bd1432209bac7c7060bd5f0c61065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Fri, 3 Jul 2020 23:41:52 +0200 Subject: [PATCH 19/26] Fixed #591 (two topics for macro editor) (#595) * Fixed #591 (crash on Macro/Add Location) The main reason was that the QSortFilterProxyModel is very sensitive to the order of signals it receives from the proxy model. In this case, dataChanged() must not be send between layoutAboutToBeChanged() and layoutChanged(). This happened implicitly during load() of a macro while scanning the freshly added folder. * Fixed another part of #591: ability to disable template selection pop-up in macro editor. Tied to the tip window now - if this is dismissed, no template selection dialog will be shown. --- src/buddies/src/bd/strmrun.cc | 1 + src/lay/lay/layMacroEditorDialog.cc | 15 ++++++---- src/laybasic/laybasic/layTipDialog.cc | 40 +++++++++++++++++++-------- src/laybasic/laybasic/layTipDialog.h | 5 ++++ src/lym/lym/lymMacro.cc | 21 +++++++------- 5 files changed, 54 insertions(+), 28 deletions(-) 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/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/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/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) { From beec19951d84bc79d1b15e22b09137d8038f7ffe Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 5 Jul 2020 14:09:23 +0200 Subject: [PATCH 20/26] Candidate for fix. --- src/pya/pya/pyaObject.cc | 6 +++++- src/rba/rba/rbaInternal.cc | 6 +++++- testdata/ruby/basic_testcore.rb | 6 ++++++ 3 files changed, 16 insertions(+), 2 deletions(-) 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/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 From 54cca8912bd78a567f664b5264478eac6cdc3eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Sun, 5 Jul 2020 09:41:44 -0700 Subject: [PATCH 21/26] Implemented #586 (issues with duplicate cell names) (#605) As discussed in the ticket, the implementation will check for unique cell names upon *writing* of a layout file. --- src/db/db/dbSaveLayoutOptions.cc | 23 +++++++++++- src/db/db/dbSaveLayoutOptions.h | 2 +- .../streamers/gds2/unit_tests/dbGDS2Writer.cc | 35 +++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) 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/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); } + From 5dce0d83843934ec76cc7c72e6e5d9d8ae87a945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Sun, 5 Jul 2020 09:42:08 -0700 Subject: [PATCH 22/26] Implemented #603 (Coordinates for adjust origin) (#604) --- .../laybasic/AlignCellOptionsDialog.ui | 82 ++++++++++++++++++- src/laybasic/laybasic/layDialogs.cc | 38 +++++++-- src/laybasic/laybasic/layDialogs.h | 18 +++- src/laybasic/laybasic/layLayoutView.cc | 18 ++-- src/laybasic/laybasic/layLayoutView.h | 5 +- .../lay_plugin/layBooleanOperationsDialogs.h | 4 +- 6 files changed, 136 insertions(+), 29 deletions(-) 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/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/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" From 8adeaaf9388447cbf847ad2e271357cec67dabe5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 5 Jul 2020 19:02:43 +0200 Subject: [PATCH 23/26] Checked add/remove methods for Netlist objects - to avoid script mistakes. --- src/db/db/dbCircuit.cc | 55 +++++++++++++++++++++++++++++++- src/db/db/dbNetlist.cc | 64 +++++++++++++++++++++++++++++++++++++- testdata/ruby/dbNetlist.rb | 25 +++++++++++++++ 3 files changed, 142 insertions(+), 2 deletions(-) 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/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/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 ]) From de9e180f3ca9493a792e8e743172a00d6809e929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Tue, 7 Jul 2020 09:24:06 -0700 Subject: [PATCH 24/26] Fixed #609 (internal error on netlist extract) (#610) Reason was: when caching instance-to-instance interactions, the array descriptions need to be normalized too. --- src/db/db/dbHierNetworkProcessor.cc | 39 +++++++++++------ src/db/db/dbHierNetworkProcessor.h | 68 ++++++++++++++++++++++++++--- 2 files changed, 88 insertions(+), 19 deletions(-) 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; }; From 848fd3e1bb993880a7c31848d1fbee0073e2d656 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 7 Jul 2020 21:21:33 +0200 Subject: [PATCH 25/26] Added testcase --- .../unit_tests/dbHierNetworkProcessorTests.cc | 67 ++++++++++++++++++ testdata/algo/issue-609.oas.gz | Bin 0 -> 418 bytes 2 files changed, 67 insertions(+) create mode 100644 testdata/algo/issue-609.oas.gz diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index 344101665..cbf29efde 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -1287,3 +1287,70 @@ 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::const_iterator c = cc.begin (); c != cc.end (); ++c) { + if (cc.is_root (c->id ())) { + ++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 and 1 to 3, but *not* 2 to 3 + 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); + + std::vector > net_layers; + + db::Layout::top_down_const_iterator td = ly.begin_top_down (); + EXPECT_EQ (td != ly.end_top_down (), true); + + // result needs to be a single net + EXPECT_EQ (root_nets (hc.clusters_per_cell (*td)), size_t (1)); + for ( ; td != ly.end_top_down (); ++td) { + EXPECT_EQ (root_nets (hc.clusters_per_cell (*td)), size_t (0)); + } +} diff --git a/testdata/algo/issue-609.oas.gz b/testdata/algo/issue-609.oas.gz new file mode 100644 index 0000000000000000000000000000000000000000..c35964b7b96ff4592dc114ee6745e57a240868b5 GIT binary patch literal 418 zcmV;T0bTwdiwFP!0000015*ul_4U;CcMSFn=H+5!HqZ@2gEzO`uOlLI~!RrGdmla zf(SDZ0pc1NfCwWHVFDt+vc@3797I@x#0)_MNX!VV4Xh3%Yh=O8CQ{ABu;o6}j}Lmi zffI8??lQJWw*ny}OHPMbhZ!#`FAGS;hcB!fjxc@v!n*MY6E8C_6G-g94$C(;Sr6{8 ze0!6Xmywr&mq+j|+tvM6Ol*@@u?W6lz5CUQLGT6J%04C_#k%kYBZJ@@kUWFnO9(F) ztl}kD1<1e+YwbH);y13f?`(pif27w5&Xe;u~BX14Od1+h8GROD{i>*vWwhj zWI**DpWp#b!2=wE2e Date: Tue, 7 Jul 2020 21:39:24 +0200 Subject: [PATCH 26/26] Fixed test. --- src/db/unit_tests/dbHierNetworkProcessorTests.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index cbf29efde..99539f854 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -1291,8 +1291,8 @@ TEST(120_HierClustersCombArrays) static size_t root_nets (const db::connected_clusters &cc) { size_t n = 0; - for (db::connected_clusters::const_iterator c = cc.begin (); c != cc.end (); ++c) { - if (cc.is_root (c->id ())) { + for (db::connected_clusters::all_iterator c = cc.begin_all (); !c.at_end (); ++c) { + if (cc.is_root (*c)) { ++n; } } @@ -1334,7 +1334,7 @@ TEST(200_issue609) normalize_layer (ly, strings, l1); normalize_layer (ly, strings, l2); - // connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3 + // connect 1 to 1, 1 to 2 db::Connectivity conn; conn.connect (l1, l1); conn.connect (l2, l2); @@ -1343,13 +1343,12 @@ TEST(200_issue609) db::hier_clusters hc; hc.build (ly, ly.cell (*ly.begin_top_down ()), conn); - std::vector > net_layers; - 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 - EXPECT_EQ (root_nets (hc.clusters_per_cell (*td)), size_t (1)); for ( ; td != ly.end_top_down (); ++td) { EXPECT_EQ (root_nets (hc.clusters_per_cell (*td)), size_t (0)); }