Merge branch 'master' into issue-588

This commit is contained in:
Matthias Koefferlein 2020-07-08 23:29:27 +02:00
commit 6aff74c844
84 changed files with 3861 additions and 1135 deletions

1
Jenkinsfile vendored
View File

@ -16,6 +16,7 @@ node("master") {
stage("Checkout sources") {
checkout scm
checkout_private()
}

View File

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

View File

@ -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<Coord> *clone () const = 0;
@ -111,8 +115,12 @@ struct ArrayBase
virtual bool equal (const ArrayBase *) const = 0;
virtual bool fuzzy_equal (const ArrayBase *) const = 0;
virtual bool less (const ArrayBase *) const = 0;
virtual bool fuzzy_less (const ArrayBase *) const = 0;
virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self = false, void *parent = 0) const = 0;
bool in_repository;
@ -554,6 +562,12 @@ struct regular_array
return (m_a == d->m_a && m_b == d->m_b && m_amax == d->m_amax && m_bmax == d->m_bmax);
}
virtual bool fuzzy_equal (const ArrayBase *b) const
{
const regular_array<Coord> *d = static_cast<const regular_array<Coord> *> (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<Coord> *d = static_cast<const regular_array<Coord> *> (b);
@ -562,6 +576,14 @@ struct regular_array
m_amax < d->m_amax || (m_amax == d->m_amax && m_bmax < d->m_bmax)))));
}
virtual bool fuzzy_less (const ArrayBase *b) const
{
const regular_array<Coord> *d = static_cast<const regular_array<Coord> *> (b);
return m_a.less (d->m_a) || (m_a.equal (d->m_a) && (
m_b.less (d->m_b) || (m_b.equal (d->m_b) && (
m_amax < d->m_amax || (m_amax == d->m_amax && m_bmax < d->m_bmax)))));
}
virtual bool is_regular_array (vector_type &a, vector_type &b, unsigned long &amax, unsigned long &bmax) const
{
a = m_a;
@ -702,6 +724,18 @@ struct regular_complex_array
return regular_array<Coord>::equal (b);
}
virtual bool fuzzy_equal (const ArrayBase *b) const
{
const regular_complex_array<Coord> *d = static_cast<const regular_complex_array<Coord> *> (b);
if (fabs (m_acos - d->m_acos) > epsilon) {
return false;
}
if (fabs (m_mag - d->m_mag) > epsilon) {
return false;
}
return regular_array<Coord>::fuzzy_equal (b);
}
virtual bool less (const ArrayBase *b) const
{
const regular_complex_array<Coord> *d = static_cast<const regular_complex_array<Coord> *> (b);
@ -714,6 +748,18 @@ struct regular_complex_array
return regular_array<Coord>::less (b);
}
virtual bool fuzzy_less (const ArrayBase *b) const
{
const regular_complex_array<Coord> *d = static_cast<const regular_complex_array<Coord> *> (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<Coord>::fuzzy_less (b);
}
virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const
{
if (!no_self) {
@ -806,6 +852,21 @@ struct iterated_array_iterator
return new iterated_array_iterator <Coord> (*this);
}
virtual size_t quad_id () const
{
return m_t.quad_id ();
}
virtual box_type quad_box () const
{
return m_t.quad_box ();
}
virtual void skip_quad ()
{
m_t.skip_quad ();
}
private:
box_tree_const_iterator m_b, m_e;
box_tree_touching_iterator m_t;
@ -964,6 +1025,20 @@ struct iterated_array
return true;
}
virtual bool fuzzy_equal (const ArrayBase *b) const
{
const iterated_array<Coord> *d = static_cast<const iterated_array<Coord> *> (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<Coord> *d = static_cast<const iterated_array<Coord> *> (b);
@ -978,6 +1053,20 @@ struct iterated_array
return false;
}
virtual bool fuzzy_less (const ArrayBase *b) const
{
const iterated_array<Coord> *d = static_cast<const iterated_array<Coord> *> (b);
if (m_v.size () != d->m_v.size ()) {
return (m_v.size () < d->m_v.size ());
}
for (const_iterator p1 = m_v.begin (), p2 = d->m_v.begin (); p1 != m_v.end (); ++p1, ++p2) {
if (! p1->equal (*p2)) {
return (p1->less (*p2));
}
}
return false;
}
virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const
{
if (!no_self) {
@ -1085,6 +1174,18 @@ struct iterated_complex_array
return iterated_array<Coord>::equal (b);
}
virtual bool fuzzy_equal (const ArrayBase *b) const
{
const iterated_complex_array<Coord> *d = static_cast<const iterated_complex_array<Coord> *> (b);
if (fabs (m_acos - d->m_acos) > epsilon) {
return false;
}
if (fabs (m_mag - d->m_mag) > epsilon) {
return false;
}
return iterated_array<Coord>::fuzzy_equal (b);
}
virtual bool less (const ArrayBase *b) const
{
const iterated_complex_array<Coord> *d = static_cast<const iterated_complex_array<Coord> *> (b);
@ -1097,6 +1198,18 @@ struct iterated_complex_array
return iterated_array<Coord>::less (b);
}
virtual bool fuzzy_less (const ArrayBase *b) const
{
const iterated_complex_array<Coord> *d = static_cast<const iterated_complex_array<Coord> *> (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<Coord>::fuzzy_less (b);
}
virtual complex_trans_type complex_trans (const simple_trans_type &s) const
{
return complex_trans_type (s, m_acos, m_mag);
@ -1187,7 +1300,12 @@ struct single_complex_inst
return true;
}
virtual bool less (const ArrayBase *b) const
virtual bool fuzzy_equal (const ArrayBase *b) const
{
return equal (b);
}
virtual bool less (const ArrayBase *b) const
{
const double epsilon = 1e-10;
const single_complex_inst<Coord> *d = static_cast<const single_complex_inst<Coord> *> (b);
@ -1200,6 +1318,11 @@ struct single_complex_inst
return false;
}
virtual bool fuzzy_less (const ArrayBase *b) const
{
return less (b);
}
virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const
{
if (!no_self) {
@ -1420,6 +1543,36 @@ struct array_iterator
return mp_base ? mp_base->index_b () : -1;
}
/**
* @brief For iterators supporting quads (iterated arrays), this method will return the quad ID
*/
size_t quad_id () const
{
return mp_base ? mp_base->quad_id () : 0;
}
/**
* @brief For iterators supporting quads (iterated arrays), this method will return the quad bounding box
*
* Note that this method will only return a valid quad box is the quad_id is non-null.
*
* This method will return the bounding box of all array offsets in the quad.
*/
db::box<Coord> quad_box () const
{
return mp_base ? mp_base->quad_box () : db::box<Coord>::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 <Coord> *mp_base;
@ -1592,6 +1745,32 @@ struct array
// .. nothing yet ..
}
/**
* @brief The iterated array constructor
*
* This is basically a convenience function that creates
* an appropriate basic_array object.
*/
template <class Iter>
array (const Obj &obj, const trans_type &trans, Iter from, Iter to)
: m_obj (obj), m_trans (trans), mp_base (new iterated_array <coord_type> (from, to))
{
// .. nothing yet ..
}
/**
* @brief The complex iterated array constructor
*
* This is basically a convenience function that creates
* an appropriate basic_array object.
*/
template <class Iter>
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 <coord_type> (ct.rcos (), ct.mag (), from, to))
{
// .. nothing yet ..
}
/**
* @brief The singular complex instance constructor
*
@ -1835,6 +2014,32 @@ struct array
}
}
/**
* @brief Gets the bounding box from the iterator's current quad
*
* The bounding box is that of all objects in the current quad and
* is confined to the array's total bounding box.
*/
template <class Iter, class BoxConv>
box_type quad_box (const Iter &iter, const BoxConv &bc) const
{
box_type bb;
if (mp_base) {
bb = mp_base->bbox (box_type (0, 0, 0, 0));
}
bb &= iter.quad_box ();
if (mp_base) {
if (mp_base->is_complex ()) {
return bb * box_type (mp_base->complex_trans (simple_trans_type (m_trans)) * bc (m_obj));
} else {
return bb * (m_trans * bc (m_obj));
}
} else {
return bb * (m_trans * bc (m_obj));
}
}
/**
* @brief The number of single instances in the array
*/
@ -1918,6 +2123,21 @@ struct array
}
}
/**
* @brief Compare operator for equality (fuzzy version)
*/
bool equal (const array<Obj, Trans> &d) const
{
if (! mp_base) {
return (m_trans.equal (d.m_trans) && m_obj == d.m_obj && ! d.mp_base);
} else {
if (! m_trans.equal (d.m_trans) || ! (m_obj == d.m_obj) || type () != d.type ()) {
return false;
}
return mp_base && mp_base->fuzzy_equal (d.mp_base);
}
}
/**
* @brief A sorting order criterion
*/
@ -1943,6 +2163,31 @@ struct array
}
}
/**
* @brief A fuzzy sorting order criterion
*/
bool less (const array<Obj, Trans> &d) const
{
if (! (m_obj == d.m_obj)) {
return (m_obj < d.m_obj);
}
if (! m_trans.equal (d.m_trans)) {
return m_trans.less (d.m_trans);
}
if (type () != d.type ()) {
return (type () < d.type ());
}
if (mp_base == d.mp_base) {
return false;
} else if (! mp_base) {
return true;
} else if (! d.mp_base) {
return false;
} else {
return mp_base->fuzzy_less (d.mp_base);
}
}
/**
* @brief Compare operator
*
@ -2263,7 +2508,7 @@ struct array
}
db::mem_stat (stat, purpose, cat, m_obj, true, (void *) this);
if (mp_base) {
db::mem_stat (stat, purpose, cat, *mp_base, false, (void *) this);
mp_base->mem_stat (stat, purpose, cat, false, (void *) this);
}
}

View File

@ -168,40 +168,43 @@ public:
typedef typename Tree::coord_type coord_type;
typedef typename Tree::box_type box_type;
box_tree_node (box_tree_node *parent, const point_type &center, unsigned int quad)
: m_center (center)
box_tree_node (box_tree_node *parent, const point_type &center, const box_type &qbox, unsigned int quad)
{
for (int i = 0; i < 5; ++i) {
m_lenq[i] = 0;
}
for (int i = 0; i < 4; ++i) {
mp_children[i] = 0;
}
mp_parent = (box_tree_node *)((char *) parent + quad);
if (parent) {
parent->mp_children [quad] = this;
point_type corner;
if (quad == 0) {
corner = qbox.upper_right ();
} else if (quad == 1) {
corner = qbox.upper_left ();
} else if (quad == 2) {
corner = qbox.lower_left ();
} else if (quad == 3) {
corner = qbox.lower_right ();
}
init (parent, center, corner, quad);
}
~box_tree_node ()
{
for (int i = 0; i < 4; ++i) {
if (mp_children [i]) {
delete mp_children [i];
mp_children [i] = 0;
box_tree_node *c = child (i);
if (c) {
delete c;
}
}
}
box_tree_node *clone (box_tree_node *parent = 0, unsigned int quad = 0) const
{
box_tree_node *n = new box_tree_node (parent, m_center, quad);
for (unsigned int i = 0; i < 5; ++i) {
n->m_lenq[i] = m_lenq[i];
}
box_tree_node *n = new box_tree_node (parent, m_center, m_corner, quad);
n->m_lenq = m_lenq;
n->m_len = m_len;
for (unsigned int i = 0; i < 4; ++i) {
if (mp_children[i]) {
mp_children[i]->clone (n, i);
box_tree_node *c = child (i);
if (c) {
c->clone (n, i);
} else {
n->m_childrefs [i] = m_childrefs [i];
}
}
return n;
@ -209,17 +212,39 @@ public:
box_tree_node *child (int i) const
{
return mp_children [i];
if ((m_childrefs [i] & 1) == 0) {
return reinterpret_cast<box_tree_node *> (m_childrefs [i]);
} else {
return 0;
}
}
void lenq (int i, size_t l)
{
m_lenq[i + 1] = l;
if (i < 0) {
m_lenq = l;
} else {
box_tree_node *c = child (i);
if (c) {
c->m_len = l;
} else {
m_childrefs [i] = l * 2 + 1;
}
}
}
size_t lenq (int i) const
{
return m_lenq[i + 1];
if (i < 0) {
return m_lenq;
} else {
box_tree_node *c = child (i);
if (c) {
return c->m_len;
} else {
return m_childrefs [i] >> 1;
}
}
}
box_tree_node *parent () const
@ -238,8 +263,8 @@ public:
stat->add (typeid (*this), (void *) this, sizeof (*this), sizeof (*this), parent, purpose, cat);
}
for (int i = 0; i < 4; ++i) {
if (mp_children [i]) {
mp_children [i]->mem_stat (stat, purpose, cat, no_self, parent);
if (child (i)) {
child (i)->mem_stat (stat, purpose, cat, no_self, parent);
}
}
}
@ -249,14 +274,52 @@ public:
return m_center;
}
box_type quad_box (int quad) const
{
box_type qb = box_type::world ();
if (parent ()) {
qb = box_type (m_corner, parent ()->center ());
}
switch (quad) {
case 0: return box_type (m_center, qb.upper_right ());
case 1: return box_type (m_center, qb.upper_left ());
case 2: return box_type (m_center, qb.lower_left ());
case 3: return box_type (m_center, qb.lower_right ());
default: return qb;
}
}
private:
box_tree_node *mp_parent;
size_t m_lenq [5];
box_tree_node *mp_children [4];
point_type m_center;
size_t m_lenq, m_len;
size_t m_childrefs [4];
point_type m_center, m_corner;
box_tree_node (const box_tree_node &d);
box_tree_node &operator= (const box_tree_node &d);
box_tree_node (box_tree_node *parent, const point_type &center, const point_type &corner, unsigned int quad)
{
init (parent, center, corner, quad);
}
void init (box_tree_node *parent, const point_type &center, const point_type &corner, unsigned int quad)
{
m_center = center;
m_corner = corner;
m_lenq = m_len = 0;
for (int i = 0; i < 4; ++i) {
m_childrefs [i] = 0;
}
mp_parent = (box_tree_node *)((char *) parent + quad);
if (parent) {
m_len = (parent->m_childrefs [quad] >> 1);
parent->m_childrefs [quad] = size_t (this);
}
}
};
/**
@ -459,28 +522,9 @@ public:
box_type quad_box () const
{
if (! mp_node) {
return box_type::world ();
} else {
point_type c = mp_node->center ();
box_type qb;
if (! mp_node->parent ()) {
qb = box_type::world ();
} else {
point_type pc = mp_node->parent ()->center ();
qb = box_type (c - (pc - c), pc);
}
switch (m_quad) {
case 0: return box_type (c, qb.upper_right ());
case 1: return box_type (c, qb.upper_left ());
case 2: return box_type (c, qb.lower_left ());
case 3: return box_type (c, qb.lower_right ());
default: return qb;
}
return mp_node->quad_box (m_quad);
}
}
@ -577,12 +621,16 @@ private:
return m_quad < 4;
}
// down one level
// down as many levels as required for the next non-empty quad
// returns true if this is possible
bool down ()
{
box_tree_node *c = mp_node->child (m_quad);
if (c) {
while (true) {
box_tree_node *c = mp_node->child (m_quad);
if (! c) {
return false;
}
mp_node = c;
m_quad = -1;
@ -595,12 +643,11 @@ private:
// nothing to visit: up again
up ();
return false;
} else {
} else if (m_quad < 0) {
// stay in main chunk
return true;
}
} else {
return false;
}
}
@ -670,7 +717,7 @@ private:
* whose box overlaps or touches a specified test box.
*/
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100>
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100, unsigned int thin_aspect = 4>
class box_tree
{
public:
@ -1175,7 +1222,16 @@ private:
// the bins are: overall, ur, ul, ll, lr, empty
element_iterator qloc [6] = { from, from, from, from, from, from };
point_type center (bbox.center ());
point_type center;
if (bbox.width () < bbox.height () / thin_aspect) {
// separate by height only
center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2);
} else if (bbox.height () < bbox.width () / thin_aspect) {
// separate by width only
center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ());
} else {
center = bbox.center ();
}
for (element_iterator e = from; e != to; ++e) {
@ -1224,7 +1280,7 @@ private:
if (nn >= min_quads) {
// create a new node representing this tree
box_tree_node *node = new box_tree_node (parent, center, quad);
box_tree_node *node = new box_tree_node (parent, center, bbox, quad);
if (parent == 0) {
mp_root = node;
}
@ -1460,28 +1516,9 @@ public:
box_type quad_box () const
{
if (! mp_node) {
return box_type::world ();
} else {
point_type c = mp_node->center ();
box_type qb;
if (! mp_node->parent ()) {
qb = box_type::world ();
} else {
point_type pc = mp_node->parent ()->center ();
qb = box_type (c - (pc - c), pc);
}
switch (m_quad) {
case 0: return box_type (c, qb.upper_right ());
case 1: return box_type (c, qb.upper_left ());
case 2: return box_type (c, qb.lower_left ());
case 3: return box_type (c, qb.lower_right ());
default: return qb;
}
return mp_node->quad_box (m_quad);
}
}
@ -1578,12 +1615,16 @@ private:
return m_quad < 4;
}
// down one level
// down as many levels as required for the next non-empty quad
// returns true if this is possible
bool down ()
{
box_tree_node *c = mp_node->child (m_quad);
if (c) {
while (true) {
box_tree_node *c = mp_node->child (m_quad);
if (! c) {
return false;
}
mp_node = c;
m_quad = -1;
@ -1596,12 +1637,11 @@ private:
// nothing to visit: up again
up ();
return false;
} else {
} else if (m_quad < 0) {
// stay in main chunk
return true;
}
} else {
return false;
}
}
@ -1638,7 +1678,7 @@ private:
* is sorted.
*/
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100>
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100, unsigned int thin_aspect = 4>
class unstable_box_tree
{
public:
@ -2103,7 +2143,16 @@ private:
}
obj_iterator qloc [5] = { from, from, from, from, from };
point_type center (bbox.center ());
point_type center;
if (bbox.width () < bbox.height () / thin_aspect) {
// separate by height only
center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2);
} else if (bbox.height () < bbox.width () / thin_aspect) {
// separate by width only
center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ());
} else {
center = bbox.center ();
}
for (obj_iterator e = from; e != to; ++e) {
@ -2158,7 +2207,7 @@ private:
if (nn >= min_quads) {
// create a new node representing this tree
box_tree_node *node = new box_tree_node (parent, center, quad);
box_tree_node *node = new box_tree_node (parent, center, bbox, quad);
if (parent == 0) {
mp_root = node;
}

View File

@ -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 <class Trans>
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.
*

View File

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

View File

@ -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 <db::CellInst, db::simple_trans<C> >::iterator i = o.begin ();
while (! (++i).at_end ()) {
h = hfunc (*i, h);
}
}
if (o.is_complex ()) {

View File

@ -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<std::pair<ClusterInstance, ClusterInstance> >::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<std::pair<ClusterInstance, ClusterInstance> >::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<InteractionKeyForClustersType, std::vector<std::pair<size_t, size_t> > >::const_iterator ici = m_interaction_cache_for_clusters.find (ikey);
if (ici != m_interaction_cache_for_clusters.end ()) {

View File

@ -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<db::basic_array<db::Coord> *> (array1)->transform (_tn);
}
if (_array2) {
array2 = _array2->basic_clone ();
static_cast<db::basic_array<db::Coord> *> (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;
};

View File

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

View File

@ -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<coord_type> a, b;
unsigned long amax, bmax;
if (ci.is_regular_array (a, b, amax, bmax)) {
if (ci.is_complex ()) {
r += " " + ci.complex_trans ().to_string ();
} else {
r += " " + (*ci.begin ()).to_string ();
}
r += " array=(" + a.to_string () + "," + b.to_string () + " " + tl::to_string (amax) + "x" + tl::to_string (bmax) + ")";
} else {
for (db::CellInstArray::iterator i = ci.begin (); ! i.at_end (); ++i) {
if (ci.is_complex ()) {
r += " " + ci.complex_trans (*i).to_string ();
} else {
r += " " + (*i).to_string ();
}
}
}
if (has_prop_id ()) {
@ -640,13 +652,12 @@ OverlappingInstanceIteratorTraits::init (instance_iterator<OverlappingInstanceIt
// ChildCellIterator implementation
ChildCellIterator::ChildCellIterator ()
: m_iter (), m_end (), mp_insts (0)
: m_iter (), m_end ()
{ }
ChildCellIterator::ChildCellIterator (const instances_type *insts)
: m_iter (insts->begin_sorted_insts ()),
m_end (insts->end_sorted_insts ()),
mp_insts (insts)
m_end (insts->end_sorted_insts ())
{ }
cell_index_type

View File

@ -945,7 +945,6 @@ public:
private:
inst_iterator_type m_iter, m_end;
const instances_type *mp_insts;
};
/**

View File

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

View File

@ -238,6 +238,7 @@ namespace tl
*/
template <> struct type_traits <db::Library> : public type_traits<void> {
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_public_destructor;
};
}

View File

@ -265,6 +265,10 @@ void Netlist::unlock ()
const tl::vector<Circuit *> &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<Circuit *> &Netlist::child_circuits (Circuit *circuit)
const tl::vector<Circuit *> &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<Circuit *> &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<db::SubCircuit *> 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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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<const db::Circuit *> &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<const db::Circuit *, CircuitMapper> &c12_circuit_and_pin_mapping, std::map<const db::Circuit *, CircuitMapper> &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<const db::Circuit *, CircuitMapper> &c12_circuit_and_pin_mapping, std::map<const db::Circuit *, CircuitMapper> &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<const db::Circuit *, CircuitMapper> &c12_circuit_and_pin_mapping, std::map<const db::Circuit *, CircuitMapper> &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;
};

View File

@ -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 <std:
}
void
SaveLayoutOptions::get_cells (const db::Layout &layout, std::set <db::cell_index_type> &cells, const std::vector <std::pair <unsigned int, db::LayerProperties> > &valid_layers) const
SaveLayoutOptions::get_cells (const db::Layout &layout, std::set <db::cell_index_type> &cells, const std::vector <std::pair <unsigned int, db::LayerProperties> > &valid_layers, bool require_unique_names) const
{
if (m_all_cells) {
@ -408,6 +409,26 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set <db::cell_index
}
}
if (require_unique_names) {
std::map<std::string, unsigned int> use_count;
for (std::set <db::cell_index_type>::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<std::string> multi;
for (std::map<std::string, unsigned int>::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

View File

@ -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 <db::cell_index_type> &cells, const std::vector <std::pair <unsigned int, db::LayerProperties> > &valid_layers) const;
void get_cells (const db::Layout &layout, std::set <db::cell_index_type> &cells, const std::vector <std::pair <unsigned int, db::LayerProperties> > &valid_layers, bool require_unique_names = true) const;
/**
* @brief Sets a layout reader option by name

View File

@ -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<OverlappingRegionTag, db::unstable_layer_tag> ();
}
}
return db::Box ();
}
template <class Iter>
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<polygon_ptr_array_iterator_type> ();
} else if (m_type == SimplePolygonPtrArray) {
do_skip_array_quad_iter<simple_polygon_ptr_array_iterator_type> ();
} else if (m_type == PathPtrArray) {
do_skip_array_quad_iter<path_ptr_array_iterator_type> ();
} else if (m_type == TextPtrArray) {
do_skip_array_quad_iter<text_ptr_array_iterator_type> ();
} else if (m_type == BoxArray) {
do_skip_array_quad_iter<box_array_iterator_type> ();
} else if (m_type == ShortBoxArray) {
do_skip_array_quad_iter<short_box_array_iterator_type> ();
}
}
}
template <class Iter>
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<polygon_ptr_array_iterator_type> ();
} else if (m_type == SimplePolygonPtrArray) {
return get_array_quad_id<simple_polygon_ptr_array_iterator_type> ();
} else if (m_type == PathPtrArray) {
return get_array_quad_id<path_ptr_array_iterator_type> ();
} else if (m_type == TextPtrArray) {
return get_array_quad_id<text_ptr_array_iterator_type> ();
} else if (m_type == BoxArray) {
return get_array_quad_id<box_array_iterator_type> ();
} else if (m_type == ShortBoxArray) {
return get_array_quad_id<short_box_array_iterator_type> ();
}
}
return 0;
}
template <class Iter, class Array>
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<typename Array::object_type> 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<polygon_ptr_array_iterator_type, polygon_ptr_array_type> ();
} else if (m_type == SimplePolygonPtrArray) {
return get_array_quad_box<simple_polygon_ptr_array_iterator_type, simple_polygon_ptr_array_type> ();
} else if (m_type == PathPtrArray) {
return get_array_quad_box<path_ptr_array_iterator_type, path_ptr_array_type> ();
} else if (m_type == TextPtrArray) {
return get_array_quad_box<text_ptr_array_iterator_type, text_ptr_array_type> ();
} else if (m_type == BoxArray) {
return get_array_quad_box<box_array_iterator_type, box_array_type> ();
} else if (m_type == ShortBoxArray) {
return get_array_quad_box<short_box_array_iterator_type, short_box_array_type> ();
}
}
return db::Box::world ();
}
void
ShapeIterator::cleanup ()
{

View File

@ -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 <class Sh, class StableTag> db::Box quad_box_by_shape (OverlappingRegionTag) const;
template <class RegionTag, class StableTag> db::Box quad_box_generic () const;
template <class Iter, class Array> db::Box get_array_quad_box () const;
template <class Iter> size_t get_array_quad_id () const;
template <class Iter> void do_skip_array_quad_iter ();
void do_skip_array_quad ();
template <class Sh, class StableTag, class RegionTag> bool advance_shape (int &mode);
template <class Array> void init_array_iter (NoRegionTag);
template <class Array> void init_array_iter (TouchingRegionTag);

View File

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

View File

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

View File

@ -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<db::CellInst, db::simple_trans<typename T::target_coord_type> > target_array;
std::vector<typename C::vector_type> iterated;
std::vector<typename target_array::vector_type> 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<typename C::vector_type>::const_iterator i = iterated.begin (); i != iterated.end (); ++i) {
iterated_transformed.push_back (t * *i);
}
if (arr.is_complex ()) {
return target_array (arr.object (), t * arr.complex_trans () * t.inverted (), iterated_transformed.begin (), iterated_transformed.end ());
} else {
return target_array (arr.object (), typename target_array::trans_type (t * typename C::complex_trans_type (arr.front ()) * t.inverted ()), iterated_transformed.begin (), iterated_transformed.end ());
}
} else if (arr.is_complex ()) {
return target_array (arr.object (), t * arr.complex_trans () * t.inverted ());
} else {
@ -352,93 +371,17 @@ struct cell_inst_array_defs
static bool less (const C *i, const C &other)
{
if (i->object ().cell_index () != other.object ().cell_index ()) {
return i->object ().cell_index () < other.object ().cell_index ();
}
db::vector<coord_type> a, b;
unsigned long na = 1, nb = 1;
bool r = i->is_regular_array (a, b, na, nb);
db::vector<coord_type> aother, bother;
unsigned long naother = 1, nbother = 1;
bool rother = other.is_regular_array (aother, bother, naother, nbother);
if (r != rother) {
return r < rother;
} else if (r) {
if (a.not_equal (aother)) {
return a.less (aother);
}
if (b.not_equal (bother)) {
return b.less (bother);
}
if (na != naother) {
return na < naother;
}
if (nb != nbother) {
return nb < nbother;
}
}
bool c = i->is_complex ();
bool cother = other.is_complex ();
if (c != cother) {
return c < cother;
} else if (c) {
return i->complex_trans ().less (other.complex_trans ());
} else {
return i->front ().less (other.front ());
}
return i->less (other);
}
static bool equal (const C *i, const C &other)
{
if (i->object ().cell_index () != other.object ().cell_index ()) {
return false;
}
db::vector<coord_type> a, b;
unsigned long na = 1, nb = 1;
bool r = i->is_regular_array (a, b, na, nb);
db::vector<coord_type> aother, bother;
unsigned long naother = 1, nbother = 1;
bool rother = other.is_regular_array (aother, bother, naother, nbother);
if (r != rother) {
return false;
} else if (r) {
if (a.not_equal (aother)) {
return false;
}
if (b.not_equal (bother)) {
return false;
}
if (na != naother) {
return false;
}
if (nb != nbother) {
return false;
}
}
bool c = i->is_complex ();
bool cother = other.is_complex ();
if (c != cother) {
return false;
} else if (c) {
return i->complex_trans ().equal (other.complex_trans ());
} else {
return i->front ().equal (other.front ());
}
return i->equal (other);
}
static bool not_equal (const C *i, const C &other)
{
return ! equal (i, other);
return ! i->equal (other);
}
static gsi::Methods methods (bool new_doc)
@ -508,7 +451,9 @@ struct cell_inst_array_defs
) +
gsi::method ("size", &C::size,
"@brief Gets the number of single instances in the array\n"
"If the instance represents a single instance, the count is 1. Otherwise it is na*nb."
"If the instance represents a single instance, the count is 1. Otherwise it is na*nb. "
"Starting with version 0.27, there may be iterated instances for which the size is larger than 1, but \\is_regular_array? will return false. "
"In this case, use \\each_trans or \\each_cplx_trans to retrieve the individual placements of the iterated instance."
) +
gsi::method_ext ("cell_index", &cell_index,
"@brief Gets the cell index of the cell instantiated \n"
@ -1618,6 +1563,28 @@ static db::Instance cell_inst_dtransform_into_cplx (db::Cell *cell, const db::In
return cell->transform_into (inst, dbu_trans.inverted () * t * dbu_trans);
}
static void cell_dtransform_simple (db::Cell *cell, const db::DTrans &t)
{
const db::Layout *layout = cell->layout ();
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer-unit transformation")));
}
db::CplxTrans dbu_trans (layout->dbu ());
cell->transform (db::Trans (dbu_trans.inverted () * db::DCplxTrans (t) * dbu_trans));
}
static void cell_dtransform_cplx (db::Cell *cell, const db::DCplxTrans &t)
{
const db::Layout *layout = cell->layout ();
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer-unit transformation")));
}
db::CplxTrans dbu_trans (layout->dbu ());
cell->transform (dbu_trans.inverted () * t * dbu_trans);
}
static void cell_dtransform_into_simple (db::Cell *cell, const db::DTrans &t)
{
const db::Layout *layout = cell->layout ();
@ -2402,6 +2369,46 @@ Class<db::Cell> 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 "

View File

@ -78,7 +78,18 @@ static std::string get_technology (db::Library *lib)
}
}
static void dummy_destroy (db::Library *) { }
Class<db::Library> 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"
) +

View File

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

View File

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

View File

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

View File

@ -1287,3 +1287,69 @@ TEST(120_HierClustersCombArrays)
run_hc_test (_this, "comb2.gds", "comb2_au1.gds");
run_hc_test_with_backannotation (_this, "comb2.gds", "comb2_au2.gds");
}
static size_t root_nets (const db::connected_clusters<db::PolygonRef> &cc)
{
size_t n = 0;
for (db::connected_clusters<db::PolygonRef>::all_iterator c = cc.begin_all (); !c.at_end (); ++c) {
if (cc.is_root (*c)) {
++n;
}
}
return n;
}
// issue #609
TEST(200_issue609)
{
db::Layout ly;
unsigned int l1 = 0, l2 = 0;
{
db::LayerProperties p;
db::LayerMap lmap;
p.layer = 1;
p.datatype = 0;
lmap.map (db::LDPair (p.layer, p.datatype), l1 = ly.insert_layer ());
ly.set_properties (l1, p);
p.layer = 2;
p.datatype = 0;
lmap.map (db::LDPair (p.layer, p.datatype), l2 = ly.insert_layer ());
ly.set_properties (l2, p);
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().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<std::string> strings;
normalize_layer (ly, strings, l1);
normalize_layer (ly, strings, l2);
// connect 1 to 1, 1 to 2
db::Connectivity conn;
conn.connect (l1, l1);
conn.connect (l2, l2);
conn.connect (l1, l2);
db::hier_clusters<db::PolygonRef> hc;
hc.build (ly, ly.cell (*ly.begin_top_down ()), conn);
db::Layout::top_down_const_iterator td = ly.begin_top_down ();
EXPECT_EQ (td != ly.end_top_down (), true);
EXPECT_EQ (root_nets (hc.clusters_per_cell (*td)), size_t (1));
++td;
// result needs to be a single net
for ( ; td != ly.end_top_down (); ++td) {
EXPECT_EQ (root_nets (hc.clusters_per_cell (*td)), size_t (0));
}
}

View File

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

View File

@ -449,6 +449,7 @@ TEST(1_SimpleInverter)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -470,6 +471,7 @@ TEST(1_SimpleInverter)
db::NetlistCrossReference xref;
db::NetlistComparer comp_xref (&xref);
comp.set_dont_consider_net_names (true);
good = comp_xref.compare (&nl1, &nl2);
@ -519,6 +521,7 @@ TEST(1_SimpleInverterMatchedDeviceClasses)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
comp.same_device_classes (nl1.device_class_by_name ("PMOS"), nl2.device_class_by_name ("PMOSB"));
bool good = comp.compare (&nl1, &nl2);
@ -571,6 +574,7 @@ TEST(1_SimpleInverterSkippedDevices)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -594,6 +598,7 @@ TEST(1_SimpleInverterSkippedDevices)
db::NetlistCrossReference xref;
db::NetlistComparer comp_xref (&xref);
comp.set_dont_consider_net_names (true);
good = comp_xref.compare (&nl1, &nl2);
@ -751,6 +756,8 @@ TEST(2_SimpleInverterWithForcedNetAssignment)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
const db::Circuit *ca = nl1.circuit_by_name ("INV");
const db::Circuit *cb = nl2.circuit_by_name ("INV");
comp.same_nets (ca->net_by_name ("VDD"), cb->net_by_name ("VDD"));
@ -797,6 +804,7 @@ TEST(3_Buffer)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -852,6 +860,7 @@ TEST(4_BufferTwoPaths)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -912,6 +921,7 @@ TEST(5_BufferTwoPathsDifferentParameters)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
// Forcing the power nets into equality makes the parameter error harder to detect
const db::Circuit *ca = nl1.circuit_by_name ("BUF");
@ -1134,6 +1144,7 @@ TEST(5_BufferTwoPathsDifferentDeviceClasses)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
// NOTE: adding this power hint makes the device class error harder to detect
const db::Circuit *ca = nl1.circuit_by_name ("BUF");
@ -1147,8 +1158,8 @@ TEST(5_BufferTwoPathsDifferentDeviceClasses)
"begin_circuit BUF BUF\n"
"match_nets INT $10\n"
"match_nets IN IN\n"
"net_mismatch OUT OUT\n"
"net_mismatch INT2 $11\n"
"net_mismatch OUT OUT\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -1199,6 +1210,7 @@ TEST(6_BufferTwoPathsAdditionalResistor)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
// Forcing the power nets into equality makes the resistor error harder to detect
const db::Circuit *ca = nl1.circuit_by_name ("BUF");
@ -1212,8 +1224,35 @@ TEST(6_BufferTwoPathsAdditionalResistor)
"begin_circuit BUF BUF\n"
"net_mismatch INT $10\n"
"match_nets IN IN\n"
"net_mismatch INT2 $11\n"
"match_nets OUT OUT\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
"match_pins $3 $2\n"
"match_devices $1 $1\n"
"match_devices $3 $2\n"
"match_devices $5 $3\n"
"match_devices $7 $4\n"
"match_devices $2 $5\n"
"match_devices $4 $6\n"
"match_devices $6 $7\n"
"match_devices $8 $8\n"
"device_mismatch (null) $9\n"
"end_circuit BUF BUF NOMATCH"
);
EXPECT_EQ (good, false);
comp.set_depth_first (false);
logger.clear ();
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit BUF BUF\n"
"net_mismatch INT $10\n"
"match_nets OUT OUT\n"
"net_mismatch INT2 $11\n"
"match_nets IN IN\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -1266,6 +1305,7 @@ TEST(6_BufferTwoPathsAdditionalDevices)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1274,9 +1314,9 @@ TEST(6_BufferTwoPathsAdditionalDevices)
"match_nets INT $11\n"
"net_mismatch VDD VDD\n"
"match_nets IN IN\n"
"net_mismatch INT2 $10\n"
"net_mismatch VSS VSS\n"
"net_mismatch OUT OUT\n"
"net_mismatch INT2 $10\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -1318,6 +1358,7 @@ TEST(7_Resistors)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1359,6 +1400,7 @@ TEST(7_ResistorsParameterMismatch)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1401,6 +1443,7 @@ TEST(7_ResistorsPlusOneDevice)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1443,6 +1486,7 @@ TEST(8_Diodes)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1484,6 +1528,7 @@ TEST(8_DiodesDontMatchOnSwappedPins)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1531,6 +1576,7 @@ TEST(10_SimpleSubCircuits)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1593,6 +1639,7 @@ TEST(10_SimpleSubCircuitsMatchedNames)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
EXPECT_EQ (good, false);
@ -1761,22 +1808,22 @@ TEST(11_MismatchingSubcircuits)
// nets are now ambiguous
EXPECT_EQ (xref2s (xref),
"TOP:TOP [Match]:\n"
" pin $0:$0 [Match]\n"
" pin $1:$1 [Match]\n"
" pin $2:$2 [Match]\n"
" pin $0:$2 [Match]\n"
" pin $1:$0 [Match]\n"
" pin $2:$1 [Match]\n"
" pin $3:$3 [Match]\n"
" net IN:OUT [MatchWithWarning]\n"
" pin $0:$0\n"
" subcircuit_pin (null):$1[$3]\n"
" net IN:IN [MatchWithWarning]\n"
" pin $0:$2\n"
" subcircuit_pin (null):$2[$1]\n"
" subcircuit_pin $1[$0]:(null)\n"
" net OUT:VDD [MatchWithWarning]\n"
" pin $1:$1\n"
" net OUT:OUT [MatchWithWarning]\n"
" pin $1:$0\n"
" subcircuit_pin (null):$1[$3]\n"
" subcircuit_pin $2[$1]:(null)\n"
" net VDD:VDD [MatchWithWarning]\n"
" pin $2:$1\n"
" subcircuit_pin (null):$1[$0]\n"
" subcircuit_pin (null):$2[$0]\n"
" subcircuit_pin $2[$1]:(null)\n"
" net VDD:IN [MatchWithWarning]\n"
" pin $2:$2\n"
" subcircuit_pin (null):$2[$1]\n"
" subcircuit_pin $1[$2]:(null)\n"
" subcircuit_pin $2[$2]:(null)\n"
" net VSS:VSS [MatchWithWarning]\n"
@ -1818,6 +1865,7 @@ TEST(12_MismatchingSubcircuitsDuplicates)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1885,6 +1933,7 @@ TEST(13_MismatchingSubcircuitsAdditionalHierarchy)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1952,6 +2001,7 @@ TEST(14_Subcircuit2Nand)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1);
bool good = comp.compare (&nl1, &nl2);
@ -2026,6 +2076,7 @@ TEST(14_Subcircuit2NandMismatchNoSwap)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
// intentionally missing: comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1);
bool good = comp.compare (&nl1, &nl2);
@ -2177,6 +2228,7 @@ TEST(14_Subcircuit2MatchWithSwap)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1);
bool good = comp.compare (&nl1, &nl2);
@ -2256,6 +2308,7 @@ TEST(15_EmptySubCircuitTest)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -2331,6 +2384,7 @@ TEST(15_EmptySubCircuitWithoutPinNames)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -2413,6 +2467,7 @@ TEST(16_UniqueSubCircuitMatching)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -2552,8 +2607,8 @@ TEST(17_InherentlyAmbiguousDecoder)
"match_ambiguous_nets NQ1 NQ1\n"
"match_ambiguous_nets NQ2 NQ2\n"
"match_nets NQ3 NQ3\n"
"match_ambiguous_nets NA NA\n"
"match_ambiguous_nets NB NB\n"
"match_nets NA NA\n"
"match_nets NB NB\n"
"match_nets B B\n"
"match_nets A A\n"
"match_pins $0 $1\n"
@ -2657,10 +2712,61 @@ TEST(17_InherentlyAmbiguousDecoder)
"match_nets B B\n"
"match_nets NB NB\n"
"match_nets NA NA\n"
"match_nets NQ0 NQ0\n"
"match_nets NQ2 NQ2\n"
"match_nets NQ1 NQ1\n"
"match_nets NQ3 NQ3\n"
"match_nets NQ2 NQ2\n"
"match_nets NQ0 NQ0\n"
"match_pins $0 $1\n"
"match_pins $1 $0\n"
"match_pins $2 $2\n"
"match_pins $3 $3\n"
"match_pins $4 $4\n"
"match_pins $5 $5\n"
"match_pins $6 $6\n"
"match_pins $7 $7\n"
"match_subcircuits $1 $1\n"
"match_subcircuits $2 $2\n"
"match_subcircuits $4 $3\n"
"match_subcircuits $6 $4\n"
"match_subcircuits $3 $5\n"
"match_subcircuits $5 $6\n"
"end_circuit DECODER DECODER MATCH"
);
EXPECT_EQ (good, true);
comp.set_depth_first (false);
logger.clear ();
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit NAND NAND\n"
"match_nets VSS VSS\n"
"match_nets INT INT\n"
"match_nets OUT OUT\n"
"match_nets VDD VDD\n"
"match_nets B B\n"
"match_nets A A\n"
"match_pins $0 $0\n"
"match_pins $1 $1\n"
"match_pins $2 $2\n"
"match_pins $3 $3\n"
"match_pins $4 $4\n"
"match_devices $1 $1\n"
"match_devices $2 $2\n"
"match_devices $3 $3\n"
"match_devices $4 $4\n"
"end_circuit NAND NAND MATCH\n"
"begin_circuit DECODER DECODER\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets NA NA\n"
"match_nets NB NB\n"
"match_nets B B\n"
"match_nets NQ1 NQ1\n"
"match_nets NQ3 NQ3\n"
"match_nets NQ2 NQ2\n"
"match_nets NQ0 NQ0\n"
"match_pins $0 $1\n"
"match_pins $1 $0\n"
"match_pins $2 $2\n"
@ -2735,6 +2841,7 @@ TEST(18_ClockTree)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (false);
bool good = comp.compare (&nl1, &nl2);
@ -2744,6 +2851,190 @@ TEST(18_ClockTree)
txt = tl::replaced (txt, "L", "X");
txt = tl::replaced (txt, "R", "X");
EXPECT_EQ (txt,
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_pins IN IN\n"
"match_pins OUT OUT\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_devices $1 $1\n"
"match_devices $2 $2\n"
"end_circuit INV INV MATCH\n"
"begin_circuit TXEE TXEE\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets S S\n"
"match_nets SX SX\n"
"match_nets SX SX\n"
"match_nets SXX SXX\n"
"match_nets SXX SXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXX SXX\n"
"match_nets SXX SXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits T T\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"end_circuit TXEE TXEE MATCH"
);
EXPECT_EQ (good, true);
logger.clear ();
comp.set_dont_consider_net_names (true);
good = comp.compare (&nl1, &nl2);
txt = logger.text ();
// because L/R matching is ambiguous, we need to do this to
// establish reproducability on different platforms:
txt = tl::replaced (txt, "L", "X");
txt = tl::replaced (txt, "R", "X");
EXPECT_EQ (txt,
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_pins IN IN\n"
"match_pins OUT OUT\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_devices $1 $1\n"
"match_devices $2 $2\n"
"end_circuit INV INV MATCH\n"
"begin_circuit TXEE TXEE\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets S S\n"
"match_ambiguous_nets SX SX\n"
"match_ambiguous_nets SX SX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits T T\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"end_circuit TXEE TXEE MATCH"
);
EXPECT_EQ (good, true);
comp.set_depth_first (false);
logger.clear ();
good = comp.compare (&nl1, &nl2);
txt = logger.text ();
// because L/R matching is ambiguous, we need to do this to
// establish reproducability on different platforms:
txt = tl::replaced (txt, "L", "X");
txt = tl::replaced (txt, "R", "X");
EXPECT_EQ (txt,
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_pins IN IN\n"
"match_pins OUT OUT\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_devices $1 $1\n"
"match_devices $2 $2\n"
"end_circuit INV INV MATCH\n"
"begin_circuit TXEE TXEE\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets S S\n"
"match_ambiguous_nets SX SX\n"
"match_ambiguous_nets SX SX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits T T\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"end_circuit TXEE TXEE MATCH"
);
EXPECT_EQ (good, true);
comp.set_depth_first (false);
logger.clear ();
good = comp.compare (&nl1, &nl2);
txt = logger.text ();
// because L/R matching is ambiguous, we need to do this to
// establish reproducability on different platforms:
txt = tl::replaced (txt, "L", "X");
txt = tl::replaced (txt, "R", "X");
EXPECT_EQ (txt,
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
@ -2903,6 +3194,7 @@ TEST(19_SymmetricCircuit)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -2922,24 +3214,123 @@ TEST(19_SymmetricCircuit)
"match_nets g1 G1\n"
"match_nets $44 YI\n"
"match_nets $14 WELL\n"
"match_nets $8 NET215\n"
"match_nets $9 NET175\n"
"match_nets $6 NET181\n"
"match_nets $4 NET200\n"
"match_nets nn1 NN1\n"
"match_ambiguous_nets nn2 NN2\n"
"match_ambiguous_nets nn2_ NN2_\n"
"match_ambiguous_nets q0 Q0\n"
"match_ambiguous_nets q1 Q1\n"
"match_nets $11 CS0\n"
"match_nets $13 CS1\n"
"match_nets nn2 NN2\n"
"match_nets nn2_ NN2_\n"
"match_nets q0 Q0\n"
"match_nets q1 Q1\n"
"match_nets q0_ Q0_\n"
"match_nets $6 NET181\n"
"match_nets nn1 NN1\n"
"match_nets $8 NET215\n"
"match_nets $13 CS1\n"
"match_nets q1_ Q1_\n"
"match_nets a0_ A0_\n"
"match_nets $34 HNET48\n"
"match_nets nn1_ NN1_\n"
"match_nets a0 A0\n"
"match_nets $35 HNET44\n"
"match_nets nn1_ NN1_\n"
"match_nets $9 NET175\n"
"match_nets $4 NET200\n"
"match_nets a0_ A0_\n"
"match_nets $34 HNET48\n"
"match_pins VDD VDD\n"
"match_pins nn1_ NN1_\n"
"match_pins nn1 NN1\n"
"match_pins q0 Q0\n"
"match_pins q0_ Q0_\n"
"match_pins q1_ Q1_\n"
"match_pins q1 Q1\n"
"match_pins nn2 NN2\n"
"match_pins nn2_ NN2_\n"
"match_pins a0 A0\n"
"match_pins a0_ A0_\n"
"match_pins g1 G1\n"
"match_pins g0 G0\n"
"match_pins gtp NN3\n"
"match_pins VSS VSS\n"
"match_pins WELL WELL\n"
"match_devices $30 0\n"
"match_devices $29 1\n"
"match_devices $9 10\n"
"match_devices $10 11\n"
"match_devices $36 12\n"
"match_devices $35 13\n"
"match_devices $34 14\n"
"match_devices $38 15\n"
"match_devices $37 16\n"
"match_devices $33 17\n"
"match_devices $27 18\n"
"match_devices $28 19\n"
"match_devices $17 2\n"
"match_devices $31 20\n"
"match_devices $32 21\n"
"match_devices $22 22\n"
"match_devices $26 23\n"
"match_devices $23 24\n"
"match_devices $43 25\n"
"match_devices $20 26\n"
"match_devices $25 27\n"
"match_devices $15 28\n"
"match_devices $14 29\n"
"match_devices $16 3\n"
"match_devices $18 30\n"
"match_devices $21 31\n"
"match_devices $13 32\n"
"match_devices $19 33\n"
"match_devices $7 34\n"
"match_devices $8 35\n"
"match_devices $24 36\n"
"match_devices $3 37\n"
"match_devices $6 38\n"
"match_devices $4 39\n"
"match_devices $39 4\n"
"match_devices $5 40\n"
"match_devices $2 41\n"
"match_devices $1 42\n"
"match_devices $40 5\n"
"match_devices $11 6\n"
"match_devices $12 7\n"
"match_devices $41 8\n"
"match_devices $42 9\n"
"end_circuit DECODE DECODE MATCH"
);
EXPECT_EQ (good, true);
comp.set_depth_first (false);
logger.clear ();
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit DECODE DECODE\n"
"match_nets $41 WL1_EN_\n"
"match_nets VDD VDD\n"
"match_nets $39 NET194\n"
"match_nets g0 G0\n"
"match_nets $40 HNET52\n"
"match_nets VSS VSS\n"
"match_nets $42 NET189\n"
"match_nets gtp NN3\n"
"match_nets $37 NET193\n"
"match_nets g1 G1\n"
"match_nets $44 YI\n"
"match_nets $14 WELL\n"
"match_ambiguous_nets nn2 NN2\n"
"match_ambiguous_nets nn2_ NN2_\n"
"match_ambiguous_nets q0 Q0\n"
"match_ambiguous_nets q1 Q1\n"
"match_nets $11 CS0\n"
"match_nets q0_ Q0_\n"
"match_nets $4 NET200\n"
"match_nets $13 CS1\n"
"match_nets q1_ Q1_\n"
"match_nets $9 NET175\n"
"match_nets a0 A0\n"
"match_nets a0_ A0_\n"
"match_nets $35 HNET44\n"
"match_nets $34 HNET48\n"
"match_nets $6 NET181\n"
"match_nets $8 NET215\n"
"match_nets nn1 NN1\n"
"match_nets nn1_ NN1_\n"
"match_pins VDD VDD\n"
"match_pins nn1_ NN1_\n"
"match_pins nn1 NN1\n"
@ -3060,6 +3451,7 @@ TEST(20_BusLikeConnections)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -3204,6 +3596,153 @@ TEST(20_BusLikeConnections)
"end_circuit TOP TOP MATCH"
);
EXPECT_EQ (good, true);
logger.clear ();
comp.set_dont_consider_net_names (false);
good = comp.compare (&nl1, &nl2);
txt = logger.text ();
EXPECT_EQ (txt,
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_pins IN IN\n"
"match_pins OUT OUT\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_devices $1 $1\n"
"match_devices $2 $2\n"
"end_circuit INV INV MATCH\n"
"begin_circuit INV8 INV8\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_ambiguous_nets IN1 A1\n"
"match_ambiguous_nets IN2 A2\n"
"match_ambiguous_nets IN3 A3\n"
"match_ambiguous_nets IN4 A4\n"
"match_ambiguous_nets IN5 A5\n"
"match_ambiguous_nets IN6 A6\n"
"match_ambiguous_nets IN7 A7\n"
"match_ambiguous_nets IN8 A8\n"
"match_nets OUT1 Q1\n"
"match_nets OUT2 Q2\n"
"match_nets OUT3 Q3\n"
"match_nets OUT4 Q4\n"
"match_nets OUT5 Q5\n"
"match_nets OUT6 Q6\n"
"match_nets OUT7 Q7\n"
"match_nets OUT8 Q8\n"
"match_pins IN1 A1\n"
"match_pins OUT1 Q1\n"
"match_pins IN2 A2\n"
"match_pins OUT2 Q2\n"
"match_pins IN3 A3\n"
"match_pins OUT3 Q3\n"
"match_pins IN4 A4\n"
"match_pins OUT4 Q4\n"
"match_pins IN5 A5\n"
"match_pins OUT5 Q5\n"
"match_pins IN6 A6\n"
"match_pins OUT6 Q6\n"
"match_pins IN7 A7\n"
"match_pins OUT7 Q7\n"
"match_pins IN8 A8\n"
"match_pins OUT8 Q8\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_subcircuits I1 I1\n"
"match_subcircuits I8 I8\n"
"match_subcircuits I3 I3\n"
"match_subcircuits I7 I7\n"
"match_subcircuits I4 I4\n"
"match_subcircuits I2 I2\n"
"match_subcircuits I6 I6\n"
"match_subcircuits I5 I5\n"
"end_circuit INV8 INV8 MATCH\n"
"begin_circuit INV8_WRAP INV8_WRAP\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets IN8 A8\n"
"match_nets OUT8 Q8\n"
"match_nets IN7 A7\n"
"match_nets OUT7 Q7\n"
"match_nets IN6 A6\n"
"match_nets OUT6 Q6\n"
"match_nets IN5 A5\n"
"match_nets OUT5 Q5\n"
"match_nets IN4 A4\n"
"match_nets OUT4 Q4\n"
"match_nets IN3 A3\n"
"match_nets OUT3 Q3\n"
"match_nets IN2 A2\n"
"match_nets OUT2 Q2\n"
"match_nets IN1 A1\n"
"match_nets OUT1 Q1\n"
"match_pins IN1 A1\n"
"match_pins OUT1 Q1\n"
"match_pins IN2 A2\n"
"match_pins OUT2 Q2\n"
"match_pins IN3 A3\n"
"match_pins OUT3 Q3\n"
"match_pins IN4 A4\n"
"match_pins OUT4 Q4\n"
"match_pins IN5 A5\n"
"match_pins OUT5 Q5\n"
"match_pins IN6 A6\n"
"match_pins OUT6 Q6\n"
"match_pins IN7 A7\n"
"match_pins OUT7 Q7\n"
"match_pins IN8 A8\n"
"match_pins OUT8 Q8\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_subcircuits INV8 INV8\n"
"end_circuit INV8_WRAP INV8_WRAP MATCH\n"
"begin_circuit TOP TOP\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets IN8 A8\n"
"match_nets OUT8 Q8\n"
"match_nets IN7 A7\n"
"match_nets OUT7 Q7\n"
"match_nets IN6 A6\n"
"match_nets OUT6 Q6\n"
"match_nets IN5 A5\n"
"match_nets OUT5 Q5\n"
"match_nets IN4 A4\n"
"match_nets OUT4 Q4\n"
"match_nets IN3 A3\n"
"match_nets OUT3 Q3\n"
"match_nets IN2 A2\n"
"match_nets OUT2 Q2\n"
"match_nets IN1 A1\n"
"match_nets OUT1 Q1\n"
"match_pins IN1 A1\n"
"match_pins OUT1 Q1\n"
"match_pins IN2 A2\n"
"match_pins OUT2 Q2\n"
"match_pins IN3 A3\n"
"match_pins OUT3 Q3\n"
"match_pins IN4 A4\n"
"match_pins OUT4 Q4\n"
"match_pins IN5 A5\n"
"match_pins OUT5 Q5\n"
"match_pins IN6 A6\n"
"match_pins OUT6 Q6\n"
"match_pins IN7 A7\n"
"match_pins OUT7 Q7\n"
"match_pins IN8 A8\n"
"match_pins OUT8 Q8\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_subcircuits INV8 INV8\n"
"end_circuit TOP TOP MATCH"
);
EXPECT_EQ (good, true);
}
TEST(21_BusLikeAmbiguousConnections)
@ -3240,6 +3779,7 @@ TEST(21_BusLikeAmbiguousConnections)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -3412,6 +3952,7 @@ TEST(22_NodesRemoved)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -3532,6 +4073,7 @@ TEST(23_NodesRemovedWithError)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -3631,6 +4173,7 @@ TEST(24_NodesRemovedButConnectedInOther)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -3722,6 +4265,7 @@ TEST(25_JoinSymmetricNets)
db::NetlistComparer comp;
comp.join_symmetric_nets (nl.circuit_by_name ("NAND2"));
comp.set_dont_consider_net_names (true);
// NOTE $1 and $2 are joined because they are symmetric
EXPECT_EQ (nl.to_string (),
@ -3760,6 +4304,7 @@ TEST(25b_JoinSymmetricNetsMultiple)
db::NetlistComparer comp;
comp.join_symmetric_nets (nl.circuit_by_name ("NAND3"));
comp.set_dont_consider_net_names (true);
nl.combine_devices ();
@ -3799,17 +4344,18 @@ TEST(25c_JoinSymmetricNetsMultipleMessedUp)
db::NetlistComparer comp;
comp.join_symmetric_nets (nl.circuit_by_name ("NOR3"));
comp.set_dont_consider_net_names (true);
nl.combine_devices ();
// NOTE $1 and $2 are joined because they are symmetric
EXPECT_EQ (nl.to_string (),
"circuit NOR3 (A=A,C=C,B=B,OUT=OUT,VSS=VSS,VDD=VDD);\n"
" device PMOS $1 (S=$5,G=B,D=$3) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $1 (S=$6,G=B,D=$3) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS $2 (S=OUT,G=A,D=VSS) (L=0.23,W=2.05,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $3 (S=$3,G=A,D=OUT) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS $4 (S=OUT,G=B,D=VSS) (L=0.23,W=2.05,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $8 (S=VDD,G=C,D=$5) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $8 (S=VDD,G=C,D=$6) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS $12 (S=VSS,G=C,D=OUT) (L=0.23,W=2.05,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
)
@ -3838,6 +4384,7 @@ TEST(26_JoinSymmetricNets)
db::NetlistComparer comp;
comp.join_symmetric_nets (nl.circuit_by_name ("RESCUBE"));
comp.set_dont_consider_net_names (true);
EXPECT_EQ (nl.to_string (),
"circuit RESCUBE (A=A,B=B);\n"
@ -3882,6 +4429,7 @@ TEST(27_DontJoinSymmetricNetsWithPins)
db::NetlistComparer comp;
comp.join_symmetric_nets (nl.circuit_by_name ("NAND2"));
comp.set_dont_consider_net_names (true);
// NOTE $1 and $2 are NOT joined because they have pins
EXPECT_EQ (nl.to_string (),
@ -3914,6 +4462,8 @@ TEST(28_NoSymmetryDetectionCases)
prep_nl (nl, nls);
db::NetlistComparer comp;
comp.set_dont_consider_net_names (true);
std::string sref = nl.to_string ();
comp.join_symmetric_nets (nl.circuit_by_name ("NAND2"));
@ -3936,6 +4486,8 @@ TEST(28_NoSymmetryDetectionCases)
prep_nl (nl, nls);
db::NetlistComparer comp;
comp.set_dont_consider_net_names (true);
std::string sref = nl.to_string ();
comp.join_symmetric_nets (nl.circuit_by_name ("NAND2"));
@ -3965,6 +4517,7 @@ TEST(28_JoinSymmetricNets)
prep_nl (nl, nls);
db::NetlistComparer comp;
comp.set_dont_consider_net_names (true);
comp.join_symmetric_nets (nl.circuit_by_name ("INV2LOAD"));
// NOTE $1 and $2 are joined because they are symmetric

View File

@ -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<bool, unsigned int> 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);
}

View File

@ -51,6 +51,9 @@ namespace {
// we do a initial scan and after this no more write access here.
typedef std::map<const std::type_info *, const ClassBase *, type_info_compare> 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<std::string, const ClassBase *> 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 std::type_info *, const ClassBase *, type_info_compare>::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;
}
}
}
}

View File

@ -37,6 +37,15 @@ See <a href="/about/lvs_ref_netter.xml#align">Netter#align</a> for a description
<p>
See <a href="/about/lvs_ref_netter.xml#compare">Netter#compare</a> for a description of that function.
</p>
<a name="consider_net_names"/><h2>"consider_net_names" - Indicates whether the netlist comparer shall use net names</h2>
<keyword name="consider_net_names"/>
<p>Usage:</p>
<ul>
<li><tt>consider_net_names(f)</tt></li>
</ul>
<p>
See <a href="/about/lvs_ref_netter.xml#consider_net_names">Netter#consider_net_names</a> for a description of that function.
</p>
<a name="equivalent_pins"/><h2>"equivalent_pins" - Marks pins as equivalent</h2>
<keyword name="equivalent_pins"/>
<p>Usage:</p>

View File

@ -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.
</p>
<a name="consider_net_names"/><h2>"consider_net_names" - Indicates whether the netlist comparer shall use net names</h2>
<keyword name="consider_net_names"/>
<p>Usage:</p>
<ul>
<li><tt>consider_net_names(f)</tt></li>
</ul>
<p>
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.
</p>
<a name="equivalent_pins"/><h2>"equivalent_pins" - Marks pins as equivalent</h2>
<keyword name="equivalent_pins"/>
<p>Usage:</p>

View File

@ -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 ("<html><body>To get started with the macro development feature, read the documentation provided: <a href=\"int:/about/macro_editor.xml\">About Macro Development</a>.</body></html>")),
"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 ("<html><body>To get started with the macro development feature, read the documentation provided: <a href=\"int:/about/macro_editor.xml\">About Macro Development</a>.</body></html>")),
"macro-editor-basic-tips");
td.exec_dialog ();
} else {
if (! cat.empty ()) {

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>432</width>
<height>342</height>
<height>349</height>
</rect>
</property>
<property name="windowTitle">
@ -32,7 +32,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Put origin relative to cell's bounding box at ...</string>
<string>Place this cell reference point ...</string>
</property>
</widget>
</item>
@ -302,6 +302,79 @@
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>... at x:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="x_le"/>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>µm, y:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="y_le"/>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="vis_only_cbx">
<property name="text">
@ -322,7 +395,7 @@
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -403,7 +476,10 @@
<tabstop>lb</tabstop>
<tabstop>cb</tabstop>
<tabstop>rb</tabstop>
<tabstop>x_le</tabstop>
<tabstop>y_le</tabstop>
<tabstop>vis_only_cbx</tabstop>
<tabstop>adjust_calls_cbx</tabstop>
</tabstops>
<resources>
<include location="../../lay/lay/layResources.qrc"/>

View File

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

View File

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

View File

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

View File

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

View File

@ -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<db::Coord>::rounded (m_align_cell_options.xpos / layout.dbu ()), -refy + db::coord_traits<db::Coord>::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<std::pair<db::Cell *, db::Instance> > insts_to_modify;
for (db::Cell::parent_inst_iterator pi = nc_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {

View File

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

View File

@ -809,15 +809,31 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
} else {
// The array (or single instance) must be iterated instance
// by instance
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ++p) {
size_t qid = 0;
// The array (or single instance) must be iterated instance by instance
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) {
test_snapshot (0);
db::ICplxTrans t (cell_inst.complex_trans (*p));
db::Box new_vp = db::Box (t.inverted () * *v);
draw_boxes (drawing_context, new_ci, trans * t, new_vp, level + 1);
if (p.quad_id () > 0 && p.quad_id () != qid) {
qid = p.quad_id ();
// if the quad is very small we don't gain anything from looking further into the quad - skip this one
db::DBox qb = trans * cell_inst.quad_box (p, bc);
if (qb.width () < 1.0 && qb.height () < 1.0) {
p.skip_quad ();
continue;
}
}
++p;
}
}
@ -986,7 +1002,6 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty
++inst;
}
}
}
@ -1582,6 +1597,8 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
db::Shape last_array;
size_t current_quad_id = 0;
size_t current_array_quad_id = 0;
db::ShapeIterator shape (shapes.begin_touching (*v, db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths, mp_prop_sel, m_inv_prop_sel));
while (! shape.at_end ()) {
@ -1596,16 +1613,18 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
}
if (skip) {
shape.skip_quad ();
continue;
}
} else {
if (shape.in_array ()) {
bool simplified = false;
if (shape.in_array () && last_array != shape.array ()) {
if (last_array != shape.array ()) {
last_array = shape.array ();
current_array_quad_id = 0;
bool simplified = false;
if (last_array.type () == db::Shape::PolygonPtrArray) {
simplified = draw_array_simplified<db::Shape::polygon_ptr_array_type> (mp_renderer.get (), last_array, frame, vertex, trans);
@ -1619,17 +1638,42 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
simplified = draw_array_simplified<db::Shape::short_box_array_type> (mp_renderer.get (), last_array, frame, vertex, trans);
}
if (simplified) {
shape.finish_array ();
// continue with the next shape, array or quad
continue;
}
}
if (simplified) {
shape.finish_array ();
} else {
mp_renderer->draw (*shape, trans, fill, frame, vertex, text);
++shape;
} else {
current_array_quad_id = 0;
}
// try whether the array quad can be simplified
size_t aqid = shape.array_quad_id ();
if (aqid != 0 && aqid != current_array_quad_id) {
current_array_quad_id = aqid;
db::DBox qbbox = trans * shape.array_quad_box ();
if (qbbox.width () < 1.5 && qbbox.height () < 1.5) {
// draw a single box instead of the quad
mp_renderer->draw (qbbox, fill, frame, vertex, text);
shape.skip_array_quad ();
// continue with the next shape, array or quad
continue;
}
}
mp_renderer->draw (*shape, trans, fill, frame, vertex, text);
++shape;
}
}
@ -1727,7 +1771,9 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
} else if (anything) {
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ++p) {
size_t qid = 0;
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) {
if (! m_draw_array_border_instances ||
p.index_a () <= 0 || (unsigned long)p.index_a () == amax - 1 || p.index_b () <= 0 || (unsigned long)p.index_b () == bmax - 1) {
@ -1736,6 +1782,21 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
db::Box new_vp = db::Box (t.inverted () * *v);
draw_layer (from_level, to_level, new_ci, trans * t, new_vp, level + 1, fill, frame, vertex, text, update_snapshot);
if (p.quad_id () > 0 && p.quad_id () != qid) {
qid = p.quad_id ();
// if the quad is very small we don't gain anything from looking further into the quad - skip this one
db::DBox qb = trans * cell_inst.quad_box (p, bc);
if (qb.width () < 1.0 && qb.height () < 1.0) {
p.skip_quad ();
continue;
}
}
++p;
}
}

View File

@ -128,12 +128,9 @@ TipDialog::no_pressed ()
accept ();
}
bool
TipDialog::do_exec_dialog (button_type *button)
static std::pair<bool, int>
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<bool, int> 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;
}
}

View File

@ -65,6 +65,11 @@ public:
*/
~TipDialog ();
/**
* @brief Returns true, if the tip dialog will be shown
*/
bool will_be_shown ();
/**
* @brief Show the dialog
*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<db::Vector> *points = 0;
if ((m & 0x8) && read_repetition ()) {
std::pair<bool, db::properties_id_type> 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 ();

View File

@ -1965,11 +1965,35 @@ OASISWriter::write (const db::CellInstArray &inst, db::properties_id_type prop_i
{
m_progress.set (mp_stream->pos ());
std::vector<db::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<db::Vector>::iterator pw = pts.begin();
for (std::vector<db::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

View File

@ -21,8 +21,8 @@
*/
#ifndef HDR_layDialogs
#define HDR_layDialogs
#ifndef HDR_layBooleanOperationsDialogs
#define HDR_layBooleanOperationsDialogs
#include "ui_BooleanOptionsDialog.h"
#include "ui_SizingOptionsDialog.h"

View File

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

View File

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

View File

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

View File

@ -1019,7 +1019,7 @@ InputPipe::read (char *b, size_t n)
size_t ret = fread (b, 1, n, m_file);
if (ret < n) {
if (ferror (m_file)) {
throw FilePReadErrorException (m_source, ferror (m_file));
throw FilePReadErrorException (m_source, errno);
}
}
@ -1049,7 +1049,7 @@ OutputPipe::OutputPipe (const std::string &path)
OutputPipe::~OutputPipe ()
{
if (m_file != NULL) {
fclose (m_file);
_pclose (m_file);
m_file = NULL;
}
}
@ -1061,7 +1061,7 @@ OutputPipe::write (const char *b, size_t n)
size_t ret = fwrite (b, 1, n, m_file);
if (ret < n) {
if (ferror (m_file)) {
throw FilePWriteErrorException (m_source, ferror (m_file));
throw FilePWriteErrorException (m_source, errno);
}
}
}
@ -1106,10 +1106,22 @@ size_t
InputPipe::read (char *b, size_t n)
{
tl_assert (m_file != NULL);
size_t ret = fread (b, 1, n, m_file);
if (ret < n) {
if (ferror (m_file)) {
throw FilePReadErrorException (m_source, ferror (m_file));
bool retry = true;
size_t ret = 0;
while (retry) {
retry = false;
ret = fread (b, 1, n, m_file);
if (ret < n) {
if (ferror (m_file)) {
if (errno != EINTR) {
throw FilePReadErrorException (m_source, errno);
} else if (ret == 0) {
retry = true;
clearerr (m_file);
}
}
}
}
@ -1138,7 +1150,7 @@ OutputPipe::OutputPipe (const std::string &path)
OutputPipe::~OutputPipe ()
{
if (m_file != NULL) {
fclose (m_file);
pclose (m_file);
m_file = NULL;
}
}
@ -1147,10 +1159,11 @@ void
OutputPipe::write (const char *b, size_t n)
{
tl_assert (m_file != NULL);
size_t ret = fwrite (b, 1, n, m_file);
if (ret < n) {
if (ferror (m_file)) {
throw FilePWriteErrorException (m_source, ferror (m_file));
if (ferror (m_file) && errno != EINTR) {
throw FilePReadErrorException (m_source, errno);
}
}
}

View File

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

View File

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

View File

@ -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 << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>";
ut::ctrl << "<testsuites>";
@ -652,8 +664,20 @@ main_cont (int &argc, char **argv)
ut::ctrl << "</testsuites>";
if (output_file) {
console.send_to (stdout);
fclose (output_file);
}
} catch (...) {
ut::ctrl << "</testsuites>";
if (output_file) {
console.send_to (stdout);
fclose (output_file);
}
throw;
}

View File

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

View File

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

BIN
testdata/algo/issue-609.oas.gz vendored Normal file

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -131,5 +131,7 @@ connect_implicit("DOESNOTEXIST", "DOESNOTEXIST")
netlist.simplify
align
consider_net_names(false)
compare

View File

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

View File

@ -131,5 +131,7 @@ connect_implicit("DOESNOTEXIST", "DOESNOTEXIST")
netlist.simplify
align
consider_net_names(false)
compare

View File

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

View File

@ -71,5 +71,7 @@ connect_global(ptie, "SUBSTRATE")
netlist.simplify
align
consider_net_names(false)
compare

View File

@ -126,5 +126,7 @@ connect_global(bulk, "SUBSTRATE")
netlist.simplify
align
consider_net_names(false)
compare

View File

@ -83,5 +83,7 @@ netlist.purge
netlist.combine_devices
netlist.purge_nets
consider_net_names(false)
compare

View File

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

View File

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

BIN
testdata/net_tracer/t6_all_nets.oas.gz.4 vendored Normal file

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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