Fixed #200 by introducing layout locking during iteration

The cause for the problem was that the layout got updated
while iterating causing the mess within the iterator.

This solution is to lock the layout while an iterator
is present. This happens for various Cell and Shapes
iterator, so it's a major enhancement.
This commit is contained in:
Matthias Koefferlein 2019-01-09 01:06:11 +01:00
parent d2d28bc613
commit aeeb6d7c87
8 changed files with 256 additions and 73 deletions

View File

@ -208,7 +208,8 @@ HEADERS = \
dbForceLink.h \
dbPlugin.h \
dbInit.h \
dbConverters.h
dbConverters.h \
gsiDeclDbHelpers.h
!equals(HAVE_QT, "0") {

View File

@ -1725,9 +1725,32 @@ public:
}
}
LayoutLocker (const LayoutLocker &other)
: mp_layout (other.mp_layout)
{
if (mp_layout) {
mp_layout->start_changes ();
}
}
LayoutLocker &operator= (const LayoutLocker &other)
{
if (this == &other) {
return *this;
}
if (mp_layout) {
mp_layout->end_changes ();
}
mp_layout = other.mp_layout;
if (mp_layout) {
mp_layout->start_changes ();
}
return *this;
}
private:
LayoutLocker (const LayoutLocker &);
LayoutLocker &operator= (const LayoutLocker &);
db::Layout *mp_layout;
};

View File

@ -23,6 +23,8 @@
#include "gsiDecl.h"
#include "gsiDeclDbHelpers.h"
#include "dbLayout.h"
#include "dbBoxConvert.h"
#include "dbRegion.h"
@ -711,70 +713,70 @@ static void dump_mem_statistics (const db::Cell *cell, bool detailed)
ms.print ();
}
static db::Shapes::shape_iterator begin_shapes (const db::Cell *s, unsigned int layer_index, unsigned int flags)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_shapes (const db::Cell *s, unsigned int layer_index, unsigned int flags)
{
return s->begin (layer_index, flags);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin (layer_index, flags));
}
static db::Shapes::shape_iterator begin_shapes_all (const db::Cell *s, unsigned int layer_index)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_shapes_all (const db::Cell *s, unsigned int layer_index)
{
return s->begin (layer_index, db::ShapeIterator::All);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin (layer_index, db::ShapeIterator::All));
}
static db::Shapes::shape_iterator begin_touching_shapes (const db::Cell *s, unsigned int layer_index, const db::Box &box, unsigned int flags)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_touching_shapes (const db::Cell *s, unsigned int layer_index, const db::Box &box, unsigned int flags)
{
return s->begin_touching (layer_index, box, flags);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (layer_index, box, flags));
}
static db::Shapes::shape_iterator begin_touching_shapes_all (const db::Cell *s, unsigned int layer_index, const db::Box &box)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_touching_shapes_all (const db::Cell *s, unsigned int layer_index, const db::Box &box)
{
return s->begin_touching (layer_index, box, db::ShapeIterator::All);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (layer_index, box, db::ShapeIterator::All));
}
static db::Shapes::shape_iterator begin_overlapping_shapes (const db::Cell *s, unsigned int layer_index, const db::Box &box, unsigned int flags)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_overlapping_shapes (const db::Cell *s, unsigned int layer_index, const db::Box &box, unsigned int flags)
{
return s->begin_overlapping (layer_index, box, flags);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (layer_index, box, flags));
}
static db::Shapes::shape_iterator begin_overlapping_shapes_all (const db::Cell *s, unsigned int layer_index, const db::Box &box)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_overlapping_shapes_all (const db::Cell *s, unsigned int layer_index, const db::Box &box)
{
return s->begin_overlapping (layer_index, box, db::ShapeIterator::All);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (layer_index, box, db::ShapeIterator::All));
}
static db::Shapes::shape_iterator begin_touching_shapes_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box, unsigned int flags)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_touching_shapes_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box, unsigned int flags)
{
const db::Layout *layout = s->layout ();
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer search box")));
}
return s->begin_touching (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, flags);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, flags));
}
static db::Shapes::shape_iterator begin_touching_shapes_all_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_touching_shapes_all_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box)
{
const db::Layout *layout = s->layout ();
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer search box")));
}
return s->begin_touching (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, db::ShapeIterator::All);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, db::ShapeIterator::All));
}
static db::Shapes::shape_iterator begin_overlapping_shapes_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box, unsigned int flags)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_overlapping_shapes_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box, unsigned int flags)
{
const db::Layout *layout = s->layout ();
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer search box")));
}
return s->begin_overlapping (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, flags);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, flags));
}
static db::Shapes::shape_iterator begin_overlapping_shapes_all_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_overlapping_shapes_all_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box)
{
const db::Layout *layout = s->layout ();
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer search box")));
}
return s->begin_overlapping (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, db::ShapeIterator::All);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, db::ShapeIterator::All));
}
static db::Instance insert_inst_with_props (db::Cell *c, const db::Cell::cell_inst_array_type &inst, db::properties_id_type id)
@ -1679,18 +1681,12 @@ static db::DBox cell_dbbox_per_layer (const db::Cell *cell, unsigned int layer_i
return cell->bbox (layer_index) * layout->dbu ();
}
static db::Cell::overlapping_iterator cell_begin_overlapping_inst_um (const db::Cell *cell, const db::DBox &db)
gsi::layout_locking_iterator1<db::Cell::overlapping_iterator> begin_overlapping_inst (const db::Cell *cell, const db::Cell::box_type &b)
{
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 search boxes")));
}
db::CplxTrans dbu_trans (layout->dbu ());
return cell->begin_overlapping (dbu_trans.inverted () * db);
return gsi::layout_locking_iterator1<db::Cell::overlapping_iterator> (cell->layout (), cell->begin_overlapping (b));
}
static db::Cell::touching_iterator cell_begin_touching_inst_um (const db::Cell *cell, const db::DBox &db)
gsi::layout_locking_iterator1<db::Cell::overlapping_iterator> begin_overlapping_inst_um (const db::Cell *cell, const db::DBox &dbox)
{
const db::Layout *layout = cell->layout ();
if (! layout) {
@ -1698,7 +1694,43 @@ static db::Cell::touching_iterator cell_begin_touching_inst_um (const db::Cell *
}
db::CplxTrans dbu_trans (layout->dbu ());
return cell->begin_touching (dbu_trans.inverted () * db);
return gsi::layout_locking_iterator1<db::Cell::overlapping_iterator> (cell->layout (), cell->begin_overlapping (dbu_trans.inverted () * dbox));
}
gsi::layout_locking_iterator1<db::Cell::touching_iterator> begin_touching_inst (const db::Cell *cell, const db::Cell::box_type &b)
{
return gsi::layout_locking_iterator1<db::Cell::touching_iterator> (cell->layout (), cell->begin_touching (b));
}
gsi::layout_locking_iterator1<db::Cell::touching_iterator> begin_touching_inst_um (const db::Cell *cell, const db::DBox &dbox)
{
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 search boxes")));
}
db::CplxTrans dbu_trans (layout->dbu ());
return gsi::layout_locking_iterator1<db::Cell::touching_iterator> (cell->layout (), cell->begin_touching (dbu_trans.inverted () * dbox));
}
gsi::layout_locking_iterator1<db::Cell::child_cell_iterator> begin_child_cells (const db::Cell *cell)
{
return gsi::layout_locking_iterator1<db::Cell::child_cell_iterator> (cell->layout (), cell->begin_child_cells ());
}
gsi::layout_locking_iterator1<db::Cell::parent_inst_iterator> begin_parent_insts (const db::Cell *cell)
{
return gsi::layout_locking_iterator1<db::Cell::parent_inst_iterator> (cell->layout (), cell->begin_parent_insts ());
}
gsi::layout_locking_iterator2<db::Cell::parent_cell_iterator> begin_parent_cells (const db::Cell *cell)
{
return gsi::layout_locking_iterator2<db::Cell::parent_cell_iterator> (cell->layout (), cell->begin_parent_cells (), cell->end_parent_cells ());
}
static layout_locking_iterator1<db::Cell::const_iterator> begin_inst (db::Cell *cell)
{
return layout_locking_iterator1<db::Cell::const_iterator> (cell->layout (), cell->begin ());
}
Class<db::Cell> decl_Cell ("db", "Cell",
@ -2569,7 +2601,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"This method has been introduced in version 0.25."
) +
gsi::iterator ("each_overlapping_inst", (db::Cell::overlapping_iterator (db::Cell::*) (const db::Cell::box_type &b) const) &db::Cell::begin_overlapping, gsi::arg ("b"),
gsi::iterator_ext ("each_overlapping_inst", &begin_overlapping_inst, gsi::arg ("b"),
"@brief Gets the instances overlapping the given rectangle\n"
"\n"
"This will iterate over all child cell\n"
@ -2579,7 +2611,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"Starting with version 0.15, this iterator delivers \\Instance objects rather than \\CellInstArray objects."
) +
gsi::iterator_ext ("each_overlapping_inst", &cell_begin_overlapping_inst_um, gsi::arg ("b"),
gsi::iterator_ext ("each_overlapping_inst", &begin_overlapping_inst_um, gsi::arg ("b"),
"@brief Gets the instances overlapping the given rectangle, with the rectangle in micrometer units\n"
"\n"
"This will iterate over all child cell\n"
@ -2592,7 +2624,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"This variant has been introduced in version 0.25."
) +
gsi::iterator ("each_touching_inst", (db::Cell::touching_iterator (db::Cell::*) (const db::Cell::box_type &b) const) &db::Cell::begin_touching, gsi::arg ("b"),
gsi::iterator_ext ("each_touching_inst", &begin_touching_inst, gsi::arg ("b"),
"@brief Gets the instances touching the given rectangle\n"
"\n"
"This will iterate over all child cell\n"
@ -2602,7 +2634,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"Starting with version 0.15, this iterator delivers \\Instance objects rather than \\CellInstArray objects."
) +
gsi::iterator_ext ("each_touching_inst", &cell_begin_touching_inst_um, gsi::arg ("b"),
gsi::iterator_ext ("each_touching_inst", &begin_touching_inst_um, gsi::arg ("b"),
"@brief Gets the instances touching the given rectangle, with the rectangle in micrometer units\n"
"\n"
"This will iterate over all child cell\n"
@ -2615,7 +2647,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"This variant has been introduced in version 0.25."
) +
gsi::iterator ("each_child_cell", &db::Cell::begin_child_cells,
gsi::iterator_ext ("each_child_cell", &begin_child_cells,
"@brief Iterates over all child cells\n"
"\n"
"This iterator will report the child cell indices, not every instance.\n"
@ -2626,12 +2658,12 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"The number of child cells (not child instances!) is returned.\n"
"CAUTION: this method is SLOW, in particular if many instances are present.\n"
) +
gsi::iterator ("each_inst", (db::Cell::const_iterator (db::Cell::*) () const) &db::Cell::begin,
gsi::iterator_ext ("each_inst", &begin_inst,
"@brief Iterates over all child instances (which may actually be instance arrays)\n"
"\n"
"Starting with version 0.15, this iterator delivers \\Instance objects rather than \\CellInstArray objects."
) +
gsi::iterator ("each_parent_inst", &db::Cell::begin_parent_insts,
gsi::iterator_ext ("each_parent_inst", &begin_parent_insts,
"@brief Iterates over the parent instance list (which may actually be instance arrays)\n"
"\n"
"The parent instances are basically inversions of the instances. Using parent instances "
@ -2642,7 +2674,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"The number of parent cells (cells which reference our cell) is reported."
) +
gsi::iterator ("each_parent_cell", &db::Cell::begin_parent_cells, &db::Cell::end_parent_cells,
gsi::iterator_ext ("each_parent_cell", &begin_parent_cells,
"@brief Iterates over all parent cells\n"
"\n"
"This iterator will iterate over the parent cells, just returning their\n"

View File

@ -0,0 +1,83 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_gsiDeclDbHelpers
#define HDR_gsiDeclDbHelpers
#include "dbLayoutUtils.h"
namespace gsi
{
/**
* @brief A safe iterator locking the layout while iterating a container within it
*/
template <class I>
class layout_locking_iterator2
: private db::LayoutLocker
{
public:
typedef typename I::value_type value_type;
typedef typename I::reference reference;
typedef typename I::pointer pointer;
typedef typename I::difference_type difference_type;
typedef typename I::iterator_category iterator_category;
layout_locking_iterator2 (const db::Layout *layout, const I &b, const I &e) : db::LayoutLocker (const_cast<db::Layout *> (layout)), m_b (b), m_e (e) {}
bool at_end () const { return m_b == m_e; }
void operator++ () { ++m_b; }
reference operator* () const { return *m_b; }
pointer operator-> () const { return m_b.operator-> (); }
private:
I m_b, m_e;
};
/**
* @brief A safe iterator locking the layout while iterating a container within it
*/
template <class I>
class layout_locking_iterator1
: private db::LayoutLocker
{
public:
typedef typename I::value_type value_type;
typedef typename I::reference reference;
typedef typename I::pointer pointer;
typedef typename I::difference_type difference_type;
typedef typename I::iterator_category iterator_category;
layout_locking_iterator1 (const db::Layout *layout, const I &i) : db::LayoutLocker (const_cast<db::Layout *> (layout)), m_i (i) { }
bool at_end () const { return m_i.at_end (); }
void operator++ () { ++m_i; }
reference operator* () const { return *m_i; }
pointer operator-> () const { return m_i.operator-> (); }
private:
I m_i;
};
}
#endif

View File

@ -22,6 +22,8 @@
#include "gsiDecl.h"
#include "gsiDeclDbHelpers.h"
#include "dbShapes.h"
#include "dbShape.h"
#include "dbLayout.h"
@ -101,54 +103,54 @@ static db::Shape dinsert_with_properties (db::Shapes *s, const Sh &p, db::proper
return s->insert (db::object_with_properties<ISh> (db::CplxTrans (shapes_dbu (s)).inverted () * p, id));
}
static db::Shapes::shape_iterator begin (const db::Shapes *s, unsigned int flags)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin (const db::Shapes *s, unsigned int flags)
{
return s->begin (flags);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin (flags));
}
static db::Shapes::shape_iterator begin_all (const db::Shapes *s)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_all (const db::Shapes *s)
{
return s->begin (db::ShapeIterator::All);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin (db::ShapeIterator::All));
}
static db::Shapes::shape_iterator begin_overlapping (const db::Shapes *s, unsigned int flags, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_overlapping (const db::Shapes *s, unsigned int flags, const db::Box &region)
{
return s->begin_overlapping (region, flags);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (region, flags));
}
static db::Shapes::shape_iterator begin_doverlapping (const db::Shapes *s, unsigned int flags, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_doverlapping (const db::Shapes *s, unsigned int flags, const db::DBox &region)
{
return s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags));
}
static db::Shapes::shape_iterator begin_overlapping_all (const db::Shapes *s, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_overlapping_all (const db::Shapes *s, const db::Box &region)
{
return s->begin_overlapping (region, db::ShapeIterator::All);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (region, db::ShapeIterator::All));
}
static db::Shapes::shape_iterator begin_doverlapping_all (const db::Shapes *s, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_doverlapping_all (const db::Shapes *s, const db::DBox &region)
{
return s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All));
}
static db::Shapes::shape_iterator begin_touching (const db::Shapes *s, unsigned int flags, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_touching (const db::Shapes *s, unsigned int flags, const db::Box &region)
{
return s->begin_touching (region, flags);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (region, flags));
}
static db::Shapes::shape_iterator begin_dtouching (const db::Shapes *s, unsigned int flags, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_dtouching (const db::Shapes *s, unsigned int flags, const db::DBox &region)
{
return s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags));
}
static db::Shapes::shape_iterator begin_touching_all (const db::Shapes *s, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_touching_all (const db::Shapes *s, const db::Box &region)
{
return s->begin_touching (region, db::ShapeIterator::All);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (region, db::ShapeIterator::All));
}
static db::Shapes::shape_iterator begin_dtouching_all (const db::Shapes *s, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_dtouching_all (const db::Shapes *s, const db::DBox &region)
{
return s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All);
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All));
}
static void transform_shapes (db::Shapes *s, const db::Trans &trans)

View File

@ -887,6 +887,21 @@ public:
init ();
}
/**
* @brief Returns a value indicating whether the given index is valid
*/
bool is_used (size_type n) const
{
if (n >= first () && n < last ()) {
if (mp_rdata) {
return mp_rdata->is_used (n);
} else {
return true;
}
}
return false;
}
/**
* @brief For diagnostics purposes only
*/
@ -908,18 +923,6 @@ private:
mp_rdata = 0;
}
bool is_used (size_type n) const
{
if (n >= first () && n < last ()) {
if (mp_rdata) {
return mp_rdata->is_used (n);
} else {
return true;
}
}
return false;
}
size_type first () const
{
if (mp_rdata) {

BIN
testdata/gds/t200.gds vendored Normal file

Binary file not shown.

View File

@ -1994,6 +1994,45 @@ END
end
# Iterating while flatten
def test_issue200
ly = RBA::Layout.new
ly.read(ENV["TESTSRC"] + "/testdata/gds/t200.gds")
l1 = ly.layer(1, 0)
l2 = ly.layer(2, 0)
l3 = ly.layer(3, 0)
tc_name = ly.top_cell.name
r1 = RBA::Region::new(ly.top_cell.begin_shapes_rec(l1))
r2 = RBA::Region::new(ly.top_cell.begin_shapes_rec(l2))
r3 = RBA::Region::new(ly.top_cell.begin_shapes_rec(l3))
assert_equal(r1.size > 0, true)
assert_equal(r2.size > 0, true)
assert_equal(r3.size == 0, true)
ly.top_cell.each_inst do |ci|
ci.flatten
end
tc = ly.cell(tc_name)
assert_equal(ly.top_cells.size, 4)
assert_equal(tc.child_cells, 0)
assert_equal(tc.parent_cells, 0)
rr1 = RBA::Region::new(tc.begin_shapes_rec(l1))
rr2 = RBA::Region::new(tc.begin_shapes_rec(l2))
rr3 = RBA::Region::new(tc.begin_shapes_rec(l3))
assert_equal(r1.size, rr1.size)
assert_equal(r2.size, rr2.size)
assert_equal(r3.size, rr3.size)
assert_equal((rr1 ^ r1).is_empty?, true)
assert_equal((rr2 ^ r2).is_empty?, true)
assert_equal((rr3 ^ r3).is_empty?, true)
end
end
load("test_epilogue.rb")