klayout/src/db/db/dbHierNetworkProcessor.cc

3411 lines
109 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2024 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbHierNetworkProcessor.h"
#include "dbShape.h"
#include "dbShapes.h"
#include "dbInstElement.h"
#include "dbPolygon.h"
#include "dbPolygonTools.h"
#include "dbBoxScanner.h"
#include "dbDeepRegion.h"
#include "dbNetShape.h"
#include "tlProgress.h"
#include "tlLog.h"
#include "tlTimer.h"
#include <vector>
#include <map>
#include <list>
#include <set>
namespace db
{
// ------------------------------------------------------------------------------
// Don't cache instance to instance interaction sets beyond this size
const size_t instance_to_instance_cache_set_size_threshold = 10000;
// ------------------------------------------------------------------------------
template <class Shape, class Trans> void insert_transformed (db::Layout &layout, db::Shapes &shapes, const Shape &s, const Trans &t);
template <class Trans> void insert_transformed (db::Layout &layout, db::Shapes &shapes, const db::PolygonRef &s, const Trans &t)
{
db::Polygon poly = s.obj ();
poly.transform (s.trans ());
if (! t.is_unity ()) {
poly.transform (t);
}
shapes.insert (db::PolygonRef (poly, layout.shape_repository ()));
}
template <class Trans> void insert_transformed (db::Layout &layout, db::Shapes &shapes, const db::NetShape &s, const Trans &t)
{
if (s.type () == db::NetShape::Polygon) {
db::PolygonRef pr = s.polygon_ref ();
db::Polygon poly = pr.obj ();
poly.transform (pr.trans ());
if (! t.is_unity ()) {
poly.transform (t);
}
shapes.insert (db::PolygonRef (poly, layout.shape_repository ()));
} else if (s.type () == db::NetShape::Text) {
db::TextRef tr = s.text_ref ();
db::Text text = tr.obj ();
text.transform (tr.trans ());
if (! t.is_unity ()) {
text.transform (t);
}
shapes.insert (db::TextRef (text, layout.shape_repository ()));
}
}
template <class Trans> void insert_transformed (db::Layout & /*layout*/, db::Shapes &shapes, const db::Edge &s, const Trans &t)
{
shapes.insert (s.transformed (t));
}
// ------------------------------------------------------------------------------
static bool is_breakout_cell (const std::set<db::cell_index_type> *breakout_cells, db::cell_index_type ci)
{
return breakout_cells && breakout_cells->find (ci) != breakout_cells->end ();
}
// ------------------------------------------------------------------------------
// Connectivity implementation
Connectivity::Connectivity ()
: m_ec (Connectivity::EdgesConnectCollinear)
{
// .. nothing yet ..
}
Connectivity::Connectivity (edge_connectivity_type ec)
: m_ec (ec)
{
// .. nothing yet ..
}
void
Connectivity::soft_connect (unsigned int la, unsigned int lb)
{
m_connected [la][lb] = -1;
m_connected [lb][la] = 1;
m_all_layers.insert (la);
m_all_layers.insert (lb);
}
void
Connectivity::connect (unsigned int la, unsigned int lb)
{
m_connected [la][lb] = 0;
m_connected [lb][la] = 0;
m_all_layers.insert (la);
m_all_layers.insert (lb);
}
void
Connectivity::connect (unsigned int l)
{
m_connected [l][l] = 0;
m_all_layers.insert (l);
}
void
Connectivity::connect (const db::DeepLayer &l)
{
connect (l.layer ());
}
void
Connectivity::connect (const db::DeepLayer &la, const db::DeepLayer &lb)
{
connect (la.layer (), lb.layer ());
}
void
Connectivity::soft_connect (const db::DeepLayer &la, const db::DeepLayer &lb)
{
soft_connect (la.layer (), lb.layer ());
}
Connectivity::global_nets_type s_empty_global_nets;
Connectivity::global_nets_iterator
Connectivity::begin_global_connections (unsigned int l) const
{
std::map<unsigned int, global_nets_type>::const_iterator g = m_global_connections.find (l);
if (g != m_global_connections.end ()) {
return g->second.begin ();
} else {
return s_empty_global_nets.begin ();
}
}
Connectivity::global_nets_iterator
Connectivity::end_global_connections (unsigned int l) const
{
std::map<unsigned int, global_nets_type>::const_iterator g = m_global_connections.find (l);
if (g != m_global_connections.end ()) {
return g->second.end ();
} else {
return s_empty_global_nets.end ();
}
}
size_t
Connectivity::connect_global (unsigned int l, const std::string &gn)
{
size_t id = global_net_id (gn);
m_global_connections [l][id] = 0;
m_all_layers.insert (l);
return id;
}
size_t
Connectivity::soft_connect_global (unsigned int l, const std::string &gn)
{
size_t id = global_net_id (gn);
m_global_connections [l][id] = -1;
m_all_layers.insert (l);
return id;
}
size_t
Connectivity::connect_global (const db::DeepLayer &l, const std::string &gn)
{
return connect_global (l.layer (), gn);
}
size_t
Connectivity::soft_connect_global (const db::DeepLayer &l, const std::string &gn)
{
return soft_connect_global (l.layer (), gn);
}
const std::string &
Connectivity::global_net_name (size_t id) const
{
tl_assert (id < m_global_net_names.size ());
return m_global_net_names [id];
}
size_t
Connectivity::global_net_id (const std::string &gn)
{
for (std::vector<std::string>::const_iterator i = m_global_net_names.begin (); i != m_global_net_names.end (); ++i) {
if (*i == gn) {
size_t id = i - m_global_net_names.begin ();
return id;
}
}
size_t id = m_global_net_names.size ();
m_global_net_names.push_back (gn);
return id;
}
size_t
Connectivity::global_nets () const
{
return m_global_net_names.size ();
}
Connectivity::all_layer_iterator
Connectivity::begin_layers () const
{
return m_all_layers.begin ();
}
Connectivity::all_layer_iterator
Connectivity::end_layers () const
{
return m_all_layers.end ();
}
Connectivity::layers_type s_empty_layers;
Connectivity::layer_iterator
Connectivity::begin_connected (unsigned int layer) const
{
std::map<unsigned int, layers_type>::const_iterator i = m_connected.find (layer);
if (i == m_connected.end ()) {
return s_empty_layers.begin ();
} else {
return i->second.begin ();
}
}
Connectivity::layer_iterator
Connectivity::end_connected (unsigned int layer) const
{
std::map<unsigned int, layers_type>::const_iterator i = m_connected.find (layer);
if (i == m_connected.end ()) {
return s_empty_layers.end ();
} else {
return i->second.end ();
}
}
template <class Trans>
static bool
interaction_test (const db::PolygonRef &a, const db::PolygonRef &b, const Trans &trans, db::Connectivity::edge_connectivity_type)
{
// TODO: this could be part of db::interact (including transformation)
if (a.obj ().is_box () && b.obj ().is_box ()) {
return db::interact (a.obj ().box ().transformed (a.trans ()), b.obj ().box ().transformed (trans * Trans (b.trans ())));
} else {
return db::interact (a.obj ().transformed (a.trans ()), b.obj ().transformed (trans * Trans (b.trans ())));
}
}
template <class C>
static bool
interaction_test (const db::PolygonRef &a, const db::PolygonRef &b, const db::unit_trans<C> &, db::Connectivity::edge_connectivity_type)
{
// TODO: this could be part of db::interact (including transformation)
if (a.obj ().is_box () && b.obj ().is_box ()) {
return db::interact (a.obj ().box ().transformed (a.trans ()), b.obj ().box ().transformed (b.trans ()));
} else {
return db::interact (a.obj ().transformed (a.trans ()), b.obj ().transformed (b.trans ()));
}
}
template <class Trans>
static bool
interaction_test (const db::NetShape &a, const db::NetShape &b, const Trans &trans, db::Connectivity::edge_connectivity_type)
{
return a.interacts_with_transformed (b, trans);
}
template <class C>
static bool
interaction_test (const db::NetShape &a, const db::NetShape &b, const db::unit_trans<C> &, db::Connectivity::edge_connectivity_type)
{
return a.interacts_with (b);
}
template <class Trans>
static bool
interaction_test (const db::Edge &a, const db::Edge &b, const Trans &trans, db::Connectivity::edge_connectivity_type ec)
{
db::Edge bt = b.transformed (trans);
if (ec == db::Connectivity::EdgesConnectByPoints) {
return a.p2 () == bt.p1 () || a.p1 () == bt.p2 ();
} else {
return a.parallel (bt) && a.intersect (bt);
}
}
template <class C>
static bool
interaction_test (const db::Edge &a, const db::Edge &b, const db::unit_trans<C> &, db::Connectivity::edge_connectivity_type ec)
{
if (ec == db::Connectivity::EdgesConnectByPoints) {
return a.p2 () == b.p1 () || a.p1 () == b.p2 ();
} else {
return a.parallel (b) && a.intersect (b);
}
}
template <class T, class Trans>
bool Connectivity::interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans, int &soft) const
{
std::map<unsigned int, layers_type>::const_iterator i = m_connected.find (la);
if (i == m_connected.end ()) {
return false;
} else {
auto t = i->second.find (lb);
if (t == i->second.end () || ! interaction_test (a, b, trans, m_ec)) {
return false;
} else {
soft = t->second;
return true;
}
}
}
bool Connectivity::interacts (const std::set<unsigned int> &la, const std::set<unsigned int> &lb) const
{
for (std::set<unsigned int>::const_iterator i = la.begin (); i != la.end (); ++i) {
db::Connectivity::layer_iterator je = end_connected (*i);
for (db::Connectivity::layer_iterator j = begin_connected (*i); j != je; ++j) {
if (lb.find (j->first) != lb.end ()) {
return true;
}
}
}
return false;
}
bool Connectivity::interact (const db::Cell &a, const db::Cell &b) const
{
for (std::map<unsigned int, layers_type>::const_iterator i = m_connected.begin (); i != m_connected.end (); ++i) {
if (! a.bbox (i->first).empty ()) {
for (layers_type::const_iterator j = i->second.begin (); j != i->second.end (); ++j) {
if (! b.bbox (j->first).empty ()) {
return true;
}
}
}
}
return false;
}
template <class T>
bool Connectivity::interact (const db::Cell &a, const T &ta, const db::Cell &b, const T &tb) const
{
for (std::map<unsigned int, layers_type>::const_iterator i = m_connected.begin (); i != m_connected.end (); ++i) {
db::Box ba = a.bbox (i->first);
if (! ba.empty ()) {
ba.transform (ta);
for (layers_type::const_iterator j = i->second.begin (); j != i->second.end (); ++j) {
db::Box bb = b.bbox (j->first);
if (! bb.empty () && bb.transformed (tb).touches (ba)) {
return true;
}
}
}
}
return false;
}
// explicit instantiations
template DB_PUBLIC bool Connectivity::interacts<db::NetShape> (const db::NetShape &a, unsigned int la, const db::NetShape &b, unsigned int lb, const db::UnitTrans &trans, int &soft) const;
template DB_PUBLIC bool Connectivity::interacts<db::NetShape> (const db::NetShape &a, unsigned int la, const db::NetShape &b, unsigned int lb, const db::ICplxTrans &trans, int &soft) const;
template DB_PUBLIC bool Connectivity::interacts<db::PolygonRef> (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::UnitTrans &trans, int &soft) const;
template DB_PUBLIC bool Connectivity::interacts<db::PolygonRef> (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::ICplxTrans &trans, int &soft) const;
template DB_PUBLIC bool Connectivity::interacts<db::Edge> (const db::Edge &a, unsigned int la, const db::Edge &b, unsigned int lb, const db::UnitTrans &trans, int &soft) const;
template DB_PUBLIC bool Connectivity::interacts<db::Edge> (const db::Edge &a, unsigned int la, const db::Edge &b, unsigned int lb, const db::ICplxTrans &trans, int &soft) const;
template DB_PUBLIC bool Connectivity::interact<db::ICplxTrans> (const db::Cell &a, const db::ICplxTrans &ta, const db::Cell &b, const db::ICplxTrans &tb) const;
// ------------------------------------------------------------------------------
// local_cluster implementation
template <class T>
local_cluster<T>::local_cluster (size_t id)
: m_id (id), m_needs_update (false), m_size (0)
{
// .. nothing yet ..
}
template <class T>
void
local_cluster<T>::clear ()
{
m_shapes.clear ();
m_needs_update = false;
m_size = 0;
m_bbox = box_type ();
m_attrs.clear ();
m_global_nets.clear ();
}
template <class T>
bool
local_cluster<T>::empty () const
{
return m_global_nets.empty () && m_shapes.empty ();
}
template <class T>
void
local_cluster<T>::set_global_nets (const global_nets &gn)
{
m_global_nets = gn;
}
template <class T>
void
local_cluster<T>::add_attr (attr_id a)
{
if (a > 0) {
m_attrs.insert (a);
}
}
template <class T>
void
local_cluster<T>::add (const T &s, unsigned int la)
{
m_shapes[la].insert (s);
m_needs_update = true;
++m_size;
}
template <class T>
void
local_cluster<T>::join_with (const local_cluster<T> &other)
{
for (typename std::map<unsigned int, tree_type>::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) {
tree_type &tree = m_shapes[s->first];
tree.insert (s->second.begin (), s->second.end ());
}
m_attrs.insert (other.m_attrs.begin (), other.m_attrs.end ());
m_global_nets.insert (other.m_global_nets.begin (), other.m_global_nets.end ());
m_size += other.size ();
m_needs_update = true;
}
template <class T>
void
local_cluster<T>::ensure_sorted ()
{
if (! m_needs_update) {
return;
}
// sort the shape trees
for (typename std::map<unsigned int, tree_type>::iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) {
s->second.sort (db::box_convert<T> ());
}
// recompute bounding box
m_bbox = box_type ();
db::box_convert<T> bc;
for (typename std::map<unsigned int, tree_type>::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) {
for (typename tree_type::const_iterator i = s->second.begin (); i != s->second.end (); ++i) {
m_bbox += bc (*i);
}
}
m_needs_update = false;
}
template <class T>
void
local_cluster<T>::mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const
{
if (! no_self) {
stat->add (typeid (*this), (void *) this, sizeof (*this), sizeof (*this), parent, purpose, cat);
}
db::mem_stat (stat, purpose, cat, m_shapes, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_attrs, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_global_nets, true, (void *) this);
}
template <class T>
class DB_PUBLIC hnp_interaction_receiver
: public box_scanner_receiver2<T, unsigned int, T, unsigned int>
{
public:
typedef typename local_cluster<T>::box_type box_type;
hnp_interaction_receiver (const Connectivity &conn, const db::ICplxTrans &trans, std::map<unsigned int, std::set<const T *> > *interacting_this, std::map<unsigned int, std::set<const T *> > *interacting_other)
: mp_conn (&conn), m_any (false), m_soft_mode (0), m_trans (trans), mp_interacting_this (interacting_this), mp_interacting_other (interacting_other)
{
// .. nothing yet ..
}
void add (const T *s1, unsigned int l1, const T *s2, unsigned int l2)
{
int soft = 0;
if (mp_conn->interacts (*s1, l1, *s2, l2, m_trans, soft)) {
if (mp_interacting_this) {
(*mp_interacting_this) [l1].insert (s1);
}
if (mp_interacting_other) {
(*mp_interacting_other) [l2].insert (s2);
}
if (soft == 0 || (m_soft_mode != 0 && m_soft_mode != soft)) {
m_soft_mode = 0;
m_any = true;
} else {
m_soft_mode = soft;
}
}
}
bool any () const
{
return m_any || m_soft_mode != 0;
}
bool stop () const
{
return m_any && ! mp_interacting_other && ! mp_interacting_this;
}
int soft_mode () const
{
return m_soft_mode;
}
private:
const Connectivity *mp_conn;
bool m_any;
int m_soft_mode;
db::ICplxTrans m_trans;
std::map<unsigned int, std::set<const T *> > *mp_interacting_this, *mp_interacting_other;
};
template <class T, class Trans>
struct transformed_box
{
typedef db::box_convert<T> base_bc;
typedef typename T::box_type box_type;
transformed_box (const Trans &trans)
: m_trans (trans)
{
// .. nothing yet ..
}
box_type operator() (const T &t) const
{
return m_bc (t).transformed (m_trans);
}
private:
base_bc m_bc;
Trans m_trans;
};
template <class T>
typename local_cluster<T>::shape_iterator local_cluster<T>::begin (unsigned int l) const
{
static tree_type s_empty_tree;
typename std::map<unsigned int, tree_type>::const_iterator i = m_shapes.find (l);
if (i == m_shapes.end ()) {
return s_empty_tree.begin_flat ();
} else {
return i->second.begin_flat ();
}
}
template <class T>
bool
local_cluster<T>::interacts (const db::Cell &cell, const db::ICplxTrans &trans, const Connectivity &conn) const
{
db::box_convert<T> bc;
for (typename std::map<unsigned int, tree_type>::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) {
db::Box box;
Connectivity::layer_iterator le = conn.end_connected (s->first);
for (Connectivity::layer_iterator l = conn.begin_connected (s->first); l != le; ++l) {
box += cell.bbox (l->first);
}
if (! box.empty () && ! s->second.begin_touching (box.transformed (trans), bc).at_end ()) {
return true;
}
}
return false;
}
template <class T>
static
void collect_interactions_in_original_order (const std::map<unsigned int, typename local_cluster<T>::tree_type> &shapes, const std::map<unsigned int, std::set<const T *> > &interacting, std::map<unsigned int, std::vector<const T *> > &interacting_out)
{
for (typename std::map<unsigned int, std::set<const T *> >::const_iterator i = interacting.begin (); i != interacting.end (); ++i) {
std::vector<const T *> &t = interacting_out [i->first];
auto s = shapes.find (i->first);
if (s == shapes.end ()) {
continue;
}
t.reserve (t.size () + i->second.size ());
for (auto j = s->second.begin (); j != s->second.end (); ++j) {
if (i->second.find (j.operator-> ()) != i->second.end ()) {
t.push_back (j.operator-> ());
}
}
}
}
template <class T>
bool
local_cluster<T>::interacts (const local_cluster<T> &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft, std::map<unsigned int, std::vector<const T *> > *interacting_this, std::map<unsigned int, std::vector<const T *> > *interacting_other) const
{
db::box_convert<T> bc;
const_cast<local_cluster<T> *> (this)->ensure_sorted ();
box_type common = other.bbox ().transformed (trans) & bbox ();
if (common.empty ()) {
return false;
}
box_type common_for_other = common.transformed (trans.inverted ());
// shortcut evaluation for disjunct layers
std::set<unsigned int> ll1;
for (typename std::map<unsigned int, tree_type>::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) {
if (! s->second.begin_touching (common, bc).at_end ()) {
ll1.insert (s->first);
}
}
if (ll1.empty ()) {
return false;
}
std::set<unsigned int> ll2;
for (typename std::map<unsigned int, tree_type>::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) {
if (! s->second.begin_touching (common_for_other, bc).at_end ()) {
ll2.insert (s->first);
}
}
if (ll2.empty ()) {
return false;
}
if (! conn.interacts (ll1, ll2)) {
return false;
}
// detailed analysis
db::box_scanner2<T, unsigned int, T, unsigned int> scanner;
transformed_box <T, db::ICplxTrans> bc_t (trans);
for (typename std::map<unsigned int, tree_type>::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) {
for (typename tree_type::touching_iterator i = s->second.begin_touching (common, bc); ! i.at_end (); ++i) {
scanner.insert1 (i.operator-> (), s->first);
}
}
for (typename std::map<unsigned int, tree_type>::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) {
for (typename tree_type::touching_iterator i = s->second.begin_touching (common_for_other, bc); ! i.at_end (); ++i) {
scanner.insert2 (i.operator-> (), s->first);
}
}
std::map<unsigned int, std::set<const T *> > is_this, is_other;
std::map<unsigned int, std::set<const T *> > *p_is_this = interacting_this ? &is_this : 0;
std::map<unsigned int, std::set<const T *> > *p_is_other = interacting_other ? &is_other : 0;
hnp_interaction_receiver<T> rec (conn, trans, p_is_this, p_is_other);
scanner.process (rec, 1 /*==touching*/, bc, bc_t);
if (rec.any ()) {
if (p_is_this) {
collect_interactions_in_original_order (m_shapes, *p_is_this, *interacting_this);
}
if (p_is_other) {
collect_interactions_in_original_order (other.m_shapes, *p_is_other, *interacting_other);
}
soft = rec.soft_mode ();
return true;
} else {
return false;
}
}
template <class T>
double local_cluster<T>::area_ratio () const
{
box_type bx = bbox ();
if (bx.empty ()) {
return 0.0;
}
db::box_convert<T> bc;
// just the sum of the areas of the bounding boxes - this is precise if no overlaps happen and the
// polygons are rather rectangular. This criterion is coarse enough to prevent recursion in the split
// algorithm and still be fine enough - consider that we a planning to use splitted polygons for
// which the bbox is a fairly good approximation.
typename box_type::area_type a = 0;
for (typename std::map<unsigned int, tree_type>::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) {
for (typename tree_type::const_iterator i = s->second.begin (); i != s->second.end (); ++i) {
a += bc (*i).area ();
}
}
return (a == 0 ? 0.0 : double (bx.area ()) / double (a));
}
template <class T>
std::vector<unsigned int>
local_cluster<T>::layers () const
{
std::vector<unsigned int> l;
l.reserve (m_shapes.size ());
for (typename std::map<unsigned int, tree_type>::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) {
l.push_back (s->first);
}
return l;
}
template <class T, class Iter>
size_t split_cluster (const local_cluster<T> &cl, double max_area_ratio, Iter &output)
{
if (cl.area_ratio () < max_area_ratio) {
return 0; // no splitting happened
}
db::box_convert<T> bc;
typename local_cluster<T>::box_type bx = cl.bbox ();
int xthr = bx.width () > bx.height () ? bx.center ().x () : bx.left ();
int ythr = bx.width () > bx.height () ? bx.bottom () : bx.center ().y ();
// split along the longer axis - decide the position according to the bbox center
local_cluster<T> a (cl.id ()), b (cl.id ());
std::vector<unsigned int> layers = cl.layers ();
for (std::vector<unsigned int>::const_iterator l = layers.begin (); l != layers.end (); ++l) {
for (typename local_cluster<T>::shape_iterator s = cl.begin (*l); ! s.at_end (); ++s) {
typename local_cluster<T>::box_type::point_type sc = bc (*s).center ();
if (sc.x () < xthr || sc.y () < ythr) {
a.add (*s, *l);
} else {
b.add (*s, *l);
}
}
}
if (a.size () == 0 || b.size () == 0) {
// give up to prevent infinite recursion
return 0;
}
// split further if required
size_t na = split_cluster (a, max_area_ratio, output);
size_t nb = split_cluster (b, max_area_ratio, output);
if (na == 0) {
*output++ = a;
na = 1;
}
if (nb == 0) {
*output++ = b;
nb = 1;
}
return na + nb;
}
template <class T>
template <class Iter>
size_t local_cluster<T>::split (double max_area_ratio, Iter &output) const
{
return split_cluster (*this, max_area_ratio, output);
}
// explicit instantiations
template class DB_PUBLIC local_cluster<db::NetShape>;
template class DB_PUBLIC local_cluster<db::PolygonRef>;
template class DB_PUBLIC local_cluster<db::Edge>;
template DB_PUBLIC size_t local_cluster<db::NetShape>::split<std::back_insert_iterator<std::list<local_cluster<db::NetShape> > > > (double, std::back_insert_iterator<std::list<local_cluster<db::NetShape> > > &) const;
template DB_PUBLIC size_t local_cluster<db::PolygonRef>::split<std::back_insert_iterator<std::list<local_cluster<db::PolygonRef> > > > (double, std::back_insert_iterator<std::list<local_cluster<db::PolygonRef> > > &) const;
template DB_PUBLIC size_t local_cluster<db::Edge>::split<std::back_insert_iterator<std::list<local_cluster<db::Edge> > > > (double, std::back_insert_iterator<std::list<local_cluster<db::Edge> > > &) const;
// ------------------------------------------------------------------------------
// local_clusters implementation
template <class T>
local_clusters<T>::local_clusters ()
: m_needs_update (false), m_next_dummy_id (0)
{
// .. nothing yet ..
}
template <class T>
void local_clusters<T>::clear ()
{
m_needs_update = false;
m_clusters.clear ();
m_bbox = box_type ();
m_next_dummy_id = 0;
m_soft_connections.clear ();
m_soft_connections_rev.clear ();
}
template <class T>
const local_cluster<T> &
local_clusters<T>::cluster_by_id (typename local_cluster<T>::id_type id) const
{
tl_assert (id > 0);
if (id > m_clusters.size ()) {
// dummy connectors are not real ones - they just carry an arbitrary
// ID. Still they need to be treated as empty ones.
static local_cluster<T> empty_cluster;
return empty_cluster;
} else {
// by convention the ID is the index + 1 so 0 can be used as "nil"
return m_clusters.objects ().item (id - 1);
}
}
template <class T>
void
local_clusters<T>::remove_cluster (typename local_cluster<T>::id_type id)
{
if (id == 0 || id > m_clusters.size ()) {
return;
}
// TODO: this const_cast is required. But we know what we're doing ...
// NOTE: we cannot really delete a cluster as this would shift the indexes. So
// we just clear them.
local_cluster<T> *to_delete = const_cast<local_cluster<T> *> (& m_clusters.objects ().item (id - 1));
to_delete->clear ();
m_needs_update = true;
// take care of soft connections
remove_soft_connections_for (id);
}
template <class T>
void
local_clusters<T>::join_cluster_with (typename local_cluster<T>::id_type id, typename local_cluster<T>::id_type with_id)
{
tl_assert (id > 0);
if (with_id > 0 && with_id <= m_clusters.size () && id <= m_clusters.size ()) {
// TODO: this const_cast is required. But we know what we're doing ...
local_cluster<T> *with = const_cast<local_cluster<T> *> (& m_clusters.objects ().item (with_id - 1));
local_cluster<T> *first = const_cast<local_cluster<T> *> (& m_clusters.objects ().item (id - 1));
first->join_with (*with);
// NOTE: we cannot really delete a cluster as this would shift the indexes. So
// we just clear them.
with->clear ();
}
// take care of soft connections
std::set<size_t> conn_down = downward_soft_connections (with_id);
std::set<size_t> conn_up = upward_soft_connections (with_id);
remove_soft_connections_for (with_id);
for (auto i = conn_down.begin (); i != conn_down.end (); ++i) {
make_soft_connection (id, *i);
}
for (auto i = conn_up.begin (); i != conn_up.end (); ++i) {
make_soft_connection (*i, id);
}
m_needs_update = true;
}
template <class T>
local_cluster<T> *
local_clusters<T>::insert ()
{
typename tree_type::iterator i = m_clusters.insert (local_cluster<T> ());
i->set_id (i.index () + 1);
m_needs_update = true;
return i.operator-> ();
}
template <class T>
void
local_clusters<T>::make_soft_connection (typename local_cluster<T>::id_type a, typename local_cluster<T>::id_type b)
{
if (a == b) {
return;
}
// NOTE: antiparallel connections are allowed. We will eliminate them later
m_soft_connections [a].insert (b);
m_soft_connections_rev [b].insert (a);
}
template <class T>
const std::set<size_t> &
local_clusters<T>::downward_soft_connections (typename local_cluster<T>::id_type id) const
{
static std::set<size_t> empty;
auto sc = m_soft_connections.find (id);
if (sc != m_soft_connections.end ()) {
return sc->second;
} else {
return empty;
}
}
template <class T>
const std::set<size_t> &
local_clusters<T>::upward_soft_connections (typename local_cluster<T>::id_type id) const
{
static std::set<size_t> empty;
auto sc = m_soft_connections_rev.find (id);
if (sc != m_soft_connections_rev.end ()) {
return sc->second;
} else {
return empty;
}
}
static void
remove_id_from_map (std::map<size_t, std::set<size_t> > &fwd, std::map<size_t, std::set<size_t> > &rev, size_t id)
{
auto sc = fwd.find (id);
if (sc != fwd.end ()) {
for (auto i = sc->second.begin (); i != sc->second.end (); ++i) {
auto sc_rev = rev.find (*i);
if (sc_rev != rev.end ()) {
sc_rev->second.erase (id);
if (sc_rev->second.empty ()) {
rev.erase (*i);
}
}
}
fwd.erase (sc);
}
}
template <class T>
void
local_clusters<T>::remove_soft_connections_for (typename local_cluster<T>::id_type id)
{
remove_id_from_map (m_soft_connections, m_soft_connections_rev, id);
remove_id_from_map (m_soft_connections_rev, m_soft_connections, id);
}
template <class T>
void
local_clusters<T>::ensure_sorted ()
{
if (! m_needs_update) {
return;
}
// sort the shape trees
m_clusters.sort (local_cluster_box_convert<T> ());
// recompute bounding box
m_bbox = box_type ();
for (typename tree_type::const_iterator i = m_clusters.begin (); i != m_clusters.end (); ++i) {
m_bbox += i->bbox ();
}
m_needs_update = false;
}
template <class T>
void
local_clusters<T>::mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const
{
if (! no_self) {
stat->add (typeid (*this), (void *) this, sizeof (*this), sizeof (*this), parent, purpose, cat);
}
db::mem_stat (stat, purpose, cat, m_needs_update, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_bbox, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_clusters, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_next_dummy_id, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_soft_connections, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_soft_connections_rev, true, (void *) this);
}
namespace
{
template <class T, class BoxTree>
struct cluster_building_receiver
: public db::box_scanner_receiver<T, std::pair<unsigned int, size_t> >
{
typedef typename local_cluster<T>::id_type id_type;
typedef std::pair<const T *, std::pair<unsigned int, size_t> > shape_value;
typedef std::vector<shape_value> shape_vector;
typedef std::set<size_t> global_nets;
typedef std::pair<shape_vector, global_nets> cluster_value;
cluster_building_receiver (const db::Connectivity &conn, const tl::equivalence_clusters<size_t> *attr_equivalence, bool separate_attributes)
: mp_conn (&conn), mp_attr_equivalence (attr_equivalence), m_separate_attributes (separate_attributes)
{
if (mp_attr_equivalence) {
// cache the global nets per attribute equivalence cluster
for (size_t gid = 0; gid < conn.global_nets (); ++gid) {
tl::equivalence_clusters<size_t>::cluster_id_type cl = attr_equivalence->cluster_id (global_net_id_to_attr (gid));
if (cl > 0) {
m_global_nets_by_attribute_cluster [cl].insert (gid);
}
}
}
}
void generate_clusters (local_clusters<T> &clusters)
{
std::map<const cluster_value *, typename local_cluster<T>::id_type> in_to_out;
// build the resulting clusters
for (typename std::list<cluster_value>::const_iterator c = m_clusters.begin (); c != m_clusters.end (); ++c) {
// TODO: reserve?
local_cluster<T> *cluster = clusters.insert ();
for (typename shape_vector::const_iterator s = c->first.begin (); s != c->first.end (); ++s) {
cluster->add (*s->first, s->second.first);
cluster->add_attr (s->second.second);
}
in_to_out.insert (std::make_pair (c.operator-> (), cluster->id ()));
std::set<size_t> global_nets = c->second;
// Add the global nets we derive from the attribute equivalence (join_nets of labelled vs.
// global nets)
if (mp_attr_equivalence) {
for (typename shape_vector::const_iterator s = c->first.begin (); s != c->first.end (); ++s) {
size_t a = s->second.second;
if (a == 0) {
continue;
}
tl::equivalence_clusters<size_t>::cluster_id_type cl = mp_attr_equivalence->cluster_id (a);
if (cl > 0) {
std::map<size_t, std::set<size_t> >::const_iterator gn = m_global_nets_by_attribute_cluster.find (cl);
if (gn != m_global_nets_by_attribute_cluster.end ()) {
global_nets.insert (gn->second.begin (), gn->second.end ());
}
}
}
}
cluster->set_global_nets (global_nets);
}
for (auto sc = m_soft_connections.begin (); sc != m_soft_connections.end (); ++sc) {
if (sc->second >= 0) {
continue;
}
auto c1 = in_to_out.find (sc->first.first);
auto c2 = in_to_out.find (sc->first.second);
tl_assert (c1 != in_to_out.end () && c2 != in_to_out.end ());
clusters.make_soft_connection (c1->second, c2->second);
}
}
void add (const T *s1, std::pair<unsigned int, size_t> p1, const T *s2, std::pair<unsigned int, size_t> p2)
{
if (m_separate_attributes && p1.second != p2.second) {
return;
}
int soft = 0;
if (! mp_conn->interacts (*s1, p1.first, *s2, p2.first, soft)) {
return;
}
typename std::map<const T *, typename std::list<cluster_value>::iterator>::iterator ic1 = m_shape_to_clusters.find (s1);
typename std::map<const T *, typename std::list<cluster_value>::iterator>::iterator ic2 = m_shape_to_clusters.find (s2);
if (ic1 == m_shape_to_clusters.end ()) {
if (ic2 == m_shape_to_clusters.end ()) {
if (soft != 0) {
m_clusters.push_back (cluster_value ());
typename std::list<cluster_value>::iterator c1 = --m_clusters.end ();
m_clusters.push_back (cluster_value ());
typename std::list<cluster_value>::iterator c2 = --m_clusters.end ();
c1->first.push_back (std::make_pair (s1, p1));
c2->first.push_back (std::make_pair (s2, p2));
m_shape_to_clusters.insert (std::make_pair (s1, c1));
m_shape_to_clusters.insert (std::make_pair (s2, c2));
register_soft_connection (c1, c2, soft);
} else {
m_clusters.push_back (cluster_value ());
typename std::list<cluster_value>::iterator c = --m_clusters.end ();
c->first.push_back (std::make_pair (s1, p1));
c->first.push_back (std::make_pair (s2, p2));
m_shape_to_clusters.insert (std::make_pair (s1, c));
m_shape_to_clusters.insert (std::make_pair (s2, c));
}
} else {
if (soft != 0) {
m_clusters.push_back (cluster_value ());
typename std::list<cluster_value>::iterator c1 = --m_clusters.end ();
c1->first.push_back (std::make_pair (s1, p1));
m_shape_to_clusters.insert (std::make_pair (s1, c1));
register_soft_connection (c1, ic2->second, soft);
} else {
ic2->second->first.push_back (std::make_pair (s1, p1));
m_shape_to_clusters.insert (std::make_pair (s1, ic2->second));
}
}
} else if (ic2 == m_shape_to_clusters.end ()) {
if (soft != 0) {
m_clusters.push_back (cluster_value ());
typename std::list<cluster_value>::iterator c2 = --m_clusters.end ();
c2->first.push_back (std::make_pair (s2, p2));
m_shape_to_clusters.insert (std::make_pair (s2, c2));
register_soft_connection (ic1->second, c2, soft);
} else {
ic1->second->first.push_back (std::make_pair (s2, p2));
m_shape_to_clusters.insert (std::make_pair (s2, ic1->second));
}
} else if (ic1->second != ic2->second) {
if (soft != 0) {
register_soft_connection (ic1->second, ic2->second, soft);
} else {
// join clusters: use the larger one as the target
if (ic1->second->first.size () < ic2->second->first.size ()) {
join (ic2->second, ic1->second);
} else {
join (ic1->second, ic2->second);
}
}
}
}
void finish (const T *s, std::pair<unsigned int, size_t> p)
{
// if the shape has not been handled yet, insert a single cluster with only this shape
typename std::map<const T *, typename std::list<cluster_value>::iterator>::iterator ic = m_shape_to_clusters.find (s);
if (ic == m_shape_to_clusters.end ()) {
m_clusters.push_back (cluster_value ());
typename std::list<cluster_value>::iterator c = --m_clusters.end ();
c->first.push_back (std::make_pair (s, p));
ic = m_shape_to_clusters.insert (std::make_pair (s, c)).first;
}
// consider connections to global nets
db::Connectivity::global_nets_iterator ge = mp_conn->end_global_connections (p.first);
for (db::Connectivity::global_nets_iterator g = mp_conn->begin_global_connections (p.first); g != ge; ++g) {
bool soft = (g->second != 0);
typename std::map<size_t, typename std::list<cluster_value>::iterator>::iterator icg = m_global_to_clusters.find (g->first);
if (icg == m_global_to_clusters.end ()) {
if (soft) {
// soft connection to a new global cluster
m_clusters.push_back (cluster_value ());
typename std::list<cluster_value>::iterator cg = --m_clusters.end ();
cg->second.insert (g->first);
m_global_to_clusters.insert (std::make_pair (g->first, cg));
register_soft_connection (ic->second, cg, g->second);
} else {
ic->second->second.insert (g->first);
m_global_to_clusters.insert (std::make_pair (g->first, ic->second));
}
} else if (ic->second != icg->second) {
if (soft) {
register_soft_connection (ic->second, icg->second, g->second);
} else {
// join clusters
if (ic->second->first.size () < icg->second->first.size ()) {
join (icg->second, ic->second);
} else {
join (ic->second, icg->second);
}
}
}
}
}
private:
const db::Connectivity *mp_conn;
std::map<const T *, typename std::list<cluster_value>::iterator> m_shape_to_clusters;
std::map<size_t, typename std::list<cluster_value>::iterator> m_global_to_clusters;
std::list<cluster_value> m_clusters;
std::map<std::pair<const cluster_value *, const cluster_value *>, int> m_soft_connections;
std::map<size_t, std::set<size_t> > m_global_nets_by_attribute_cluster;
const tl::equivalence_clusters<size_t> *mp_attr_equivalence;
bool m_separate_attributes;
void join (typename std::list<cluster_value>::iterator ic1, typename std::list<cluster_value>::iterator ic2)
{
if (ic1 == ic2) {
return;
}
ic1->first.insert (ic1->first.end (), ic2->first.begin (), ic2->first.end ());
ic1->second.insert (ic2->second.begin (), ic2->second.end ());
for (typename shape_vector::const_iterator i = ic2->first.begin (); i != ic2->first.end (); ++i) {
m_shape_to_clusters [i->first] = ic1;
}
for (typename global_nets::const_iterator i = ic2->second.begin (); i != ic2->second.end (); ++i) {
m_global_to_clusters [*i] = ic1;
}
m_clusters.erase (ic2);
// remap soft connections: remove soft connections between ic1 and ic2 and
// remap ic2 connections to ic1 as first
auto i2 = m_soft_connections.lower_bound (std::make_pair (ic2.operator-> (), (const cluster_value *) 0));
for (auto i = i2; i != m_soft_connections.end () && i->first.first == ic2.operator-> (); ++i) {
m_soft_connections.erase (std::make_pair (i->first.second, i->first.first));
}
for (auto i = i2; i != m_soft_connections.end () && i->first.first == ic2.operator-> (); ++i) {
if (i->first.second != ic1.operator-> ()) {
register_soft_connection (ic1.operator-> (), i->first.second, i->second);
}
}
auto i2_from = i2;
for ( ; i2 != m_soft_connections.end () && i2->first.first == ic2.operator-> (); ++i2)
;
m_soft_connections.erase (i2_from, i2);
}
void register_soft_connection (typename std::list<cluster_value>::iterator ic1, typename std::list<cluster_value>::iterator ic2, int soft)
{
register_soft_connection (ic1.operator-> (), ic2.operator-> (), soft);
}
void register_soft_connection (const cluster_value *c1, const cluster_value *c2, int soft)
{
auto i1 = m_soft_connections.find (std::make_pair (c1, c2));
auto i2 = m_soft_connections.find (std::make_pair (c2, c1));
if (i1 != m_soft_connections.end () && i1->second != 0 && i1->second != soft) {
i1->second = 0;
}
if (i2 != m_soft_connections.end () && i2->second != 0 && i2->second != -soft) {
i2->second = 0;
}
if (i1 == m_soft_connections.end ()) {
tl_assert (i2 == m_soft_connections.end ());
m_soft_connections.insert (std::make_pair (std::make_pair (c1, c2), soft));
m_soft_connections.insert (std::make_pair (std::make_pair (c2, c1), -soft));
}
}
};
template <class T>
struct attr_accessor
{
size_t operator() (const db::Shape &shape) const
{
return shape.prop_id ();
}
};
template <>
struct attr_accessor<db::NetShape>
{
size_t operator() (const db::Shape &shape) const
{
// NOTE: the attribute is
// * odd: a StringRef pointer's value
// * even: a Property ID times 2
if (shape.type () == db::Shape::TextRef) {
return db::text_ref_to_attr (&shape.text_ref ().obj ());
} else {
return db::prop_id_to_attr (shape.prop_id ());
}
}
};
template <class T> struct get_shape_flags { };
template <>
struct get_shape_flags<db::Edge>
{
db::ShapeIterator::flags_type operator() () const
{
return db::ShapeIterator::Edges;
}
};
template <>
struct get_shape_flags<db::PolygonRef>
{
db::ShapeIterator::flags_type operator() () const
{
return db::ShapeIterator::Polygons;
}
};
template <>
struct get_shape_flags<db::NetShape>
{
db::ShapeIterator::flags_type operator() () const
{
return db::ShapeIterator::flags_type (db::ShapeIterator::Polygons | db::ShapeIterator::Texts);
}
};
}
template <class T>
void
local_clusters<T>::build_clusters (const db::Cell &cell, const db::Connectivity &conn, const tl::equivalence_clusters<size_t> *attr_equivalence, bool report_progress, bool separate_attributes)
{
static std::string desc = tl::to_string (tr ("Building local clusters"));
db::box_scanner<T, std::pair<unsigned int, size_t> > bs (report_progress, desc);
db::box_convert<T> bc;
addressable_object_from_shape<T> heap;
attr_accessor<T> attr;
db::ShapeIterator::flags_type shape_flags = get_shape_flags<T> () ();
for (db::Connectivity::all_layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
const db::Shapes &shapes = cell.shapes (*l);
for (db::Shapes::shape_iterator s = shapes.begin (shape_flags); ! s.at_end (); ++s) {
bs.insert (heap (*s), std::make_pair (*l, attr (*s)));
}
}
cluster_building_receiver<T, box_type> rec (conn, attr_equivalence, separate_attributes);
bs.process (rec, 1 /*==touching*/, bc);
rec.generate_clusters (*this);
if (attr_equivalence && attr_equivalence->size () > 0) {
apply_attr_equivalences (*attr_equivalence);
}
}
template <class T>
void
local_clusters<T>::apply_attr_equivalences (const tl::equivalence_clusters<size_t> &attr_equivalence)
{
// identify the layout clusters joined into one attribute cluster and join them
// NOTE: soft connections are not supported here, but that is a deprecated mechanism anyway.
std::map<tl::equivalence_clusters<size_t>::cluster_id_type, std::set<size_t> > c2c;
for (const_iterator c = begin (); c != end (); ++c) {
for (typename local_cluster<T>::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) {
tl::equivalence_clusters<size_t>::cluster_id_type cl = attr_equivalence.cluster_id (*a);
if (cl > 0) {
c2c [cl].insert (c->id ());
}
}
for (typename local_cluster<T>::global_nets_iterator g = c->begin_global_nets (); g != c->end_global_nets (); ++g) {
size_t a = global_net_id_to_attr (*g);
tl::equivalence_clusters<size_t>::cluster_id_type cl = attr_equivalence.cluster_id (a);
if (cl > 0) {
c2c [cl].insert (c->id ());
}
}
}
for (std::map<tl::equivalence_clusters<size_t>::cluster_id_type, std::set<size_t> >::const_iterator c = c2c.begin (); c != c2c.end (); ++c) {
if (c->second.size () > 1) {
std::set<size_t>::const_iterator cl0 = c->second.begin ();
std::set<size_t>::const_iterator cl = cl0;
while (++cl != c->second.end ()) {
join_cluster_with (*cl0, *cl);
}
}
}
}
// explicit instantiations
template class DB_PUBLIC local_clusters<db::NetShape>;
template class DB_PUBLIC local_clusters<db::PolygonRef>;
template class DB_PUBLIC local_clusters<db::Edge>;
// ------------------------------------------------------------------------------
// connected_clusters_iterator implementation
template <class T>
connected_clusters_iterator<T>::connected_clusters_iterator (const connected_clusters<T> &c)
: m_lc_iter (c.begin ())
{
size_t max_id = 0;
for (typename connected_clusters<T>::const_iterator i = c.begin (); i != c.end (); ++i) {
if (i->id () > max_id) {
max_id = i->id ();
}
}
m_x_iter = c.m_connections.lower_bound (max_id + 1);
m_x_iter_end = c.m_connections.end ();
}
// explicit instantiations
template class DB_PUBLIC connected_clusters_iterator<db::NetShape>;
template class DB_PUBLIC connected_clusters_iterator<db::PolygonRef>;
template class DB_PUBLIC connected_clusters_iterator<db::Edge>;
// ------------------------------------------------------------------------------
// connected_clusters implementation
template <class T>
const typename connected_clusters<T>::connections_type &
connected_clusters<T>::connections_for_cluster (typename local_cluster<T>::id_type id) const
{
typename std::map<typename local_cluster<T>::id_type, connections_type>::const_iterator c = m_connections.find (id);
if (c == m_connections.end ()) {
static connections_type empty_connections;
return empty_connections;
} else {
return c->second;
}
}
template <class T>
void
connected_clusters<T>::add_connection (typename local_cluster<T>::id_type id, const ClusterInstance &inst)
{
m_connections [id].push_back (inst);
m_rev_connections [inst] = id;
}
template <class T>
void
connected_clusters<T>::join_cluster_with (typename local_cluster<T>::id_type id, typename local_cluster<T>::id_type with_id)
{
if (id == with_id) {
return;
}
local_clusters<T>::join_cluster_with (id, with_id);
// handle the connections by translating
typename std::map<typename local_cluster<T>::id_type, connections_type>::iterator tc = m_connections.find (with_id);
if (tc != m_connections.end ()) {
connections_type &to_join = tc->second;
for (connections_type::const_iterator c = to_join.begin (); c != to_join.end (); ++c) {
m_rev_connections [*c] = id;
}
connections_type &target = m_connections [id];
target.splice (to_join);
m_connections.erase (tc);
}
}
template <class T>
typename local_cluster<T>::id_type
connected_clusters<T>::find_cluster_with_connection (const ClusterInstance &inst) const
{
typename std::map<ClusterInstance, typename local_cluster<T>::id_type>::const_iterator rc = m_rev_connections.find (inst);
if (rc != m_rev_connections.end ()) {
return rc->second;
} else {
return 0;
}
}
template <class T>
void
connected_clusters<T>::mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const
{
if (! no_self) {
stat->add (typeid (*this), (void *) this, sizeof (*this), sizeof (*this), parent, purpose, cat);
}
local_clusters<T>::mem_stat (stat, purpose, cat, true, parent);
db::mem_stat (stat, purpose, cat, m_connections, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_rev_connections, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_connected_clusters, true, (void *) this);
}
// explicit instantiations
template class DB_PUBLIC connected_clusters<db::NetShape>;
template class DB_PUBLIC connected_clusters<db::PolygonRef>;
template class DB_PUBLIC connected_clusters<db::Edge>;
// ------------------------------------------------------------------------------
// connected_clusters implementation
template <class T>
class DB_PUBLIC cell_clusters_box_converter
{
public:
typedef db::simple_bbox_tag complexity;
typedef typename hier_clusters<T>::box_type box_type;
cell_clusters_box_converter (const db::Layout &layout, const hier_clusters<T> &tree)
: mp_layout (&layout), mp_tree (&tree)
{
// .. nothing yet ..
}
const box_type &operator() (const db::CellInst &cell_inst) const
{
return (*this) (cell_inst.cell_index ());
}
const box_type &operator() (db::cell_index_type cell_index) const
{
typename std::map<db::cell_index_type, box_type>::const_iterator b = m_cache.find (cell_index);
if (b != m_cache.end ()) {
return b->second;
} else {
const connected_clusters<T> &clusters = mp_tree->clusters_per_cell (cell_index);
box_type box = clusters.bbox ();
const db::Cell &cell = mp_layout->cell (cell_index);
for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) {
const db::CellInstArray &inst_array = inst->cell_inst ();
box += inst_array.bbox (*this);
}
return m_cache.insert (std::make_pair (cell_index, box)).first->second;
}
}
private:
mutable std::map<db::cell_index_type, box_type> m_cache;
const db::Layout *mp_layout;
const hier_clusters<T> *mp_tree;
};
// ------------------------------------------------------------------------------
// hier_clusters implementation
template <class T>
const db::cell_index_type hier_clusters<T>::top_cell_index = std::numeric_limits<db::cell_index_type>::max ();
template <class T>
hier_clusters<T>::hier_clusters ()
: m_base_verbosity (20)
{
// .. nothing yet ..
}
template <class T>
void hier_clusters<T>::set_base_verbosity (int bv)
{
m_base_verbosity = bv;
}
template <class T>
void hier_clusters<T>::clear ()
{
m_per_cell_clusters.clear ();
}
template <class T>
void
hier_clusters<T>::mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const
{
if (! no_self) {
stat->add (typeid (*this), (void *) this, sizeof (*this), sizeof (*this), parent, purpose, cat);
}
db::mem_stat (stat, purpose, cat, m_per_cell_clusters, true, (void *) this);
}
template <class T>
void
hier_clusters<T>::build (const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::map<db::cell_index_type, tl::equivalence_clusters<size_t> > *attr_equivalence, const std::set<db::cell_index_type> *breakout_cells, bool separate_attributes)
{
clear ();
cell_clusters_box_converter<T> cbc (layout, *this);
do_build (cbc, layout, cell, conn, attr_equivalence, breakout_cells, separate_attributes);
}
namespace
{
/**
* @brief The central interaction tester between clusters on a hierarchical level
*
* This receiver is both used for the instance-to-instance and the local-to-instance
* interactions. It is employed on cell level for in two box scanners: one
* investigating the instance-to-instance interactions and another one investigating
* local cluster to instance interactions.
*/
template <class T>
struct hc_receiver
: public db::box_scanner_receiver<db::Instance, unsigned int>,
public db::box_scanner_receiver2<local_cluster<T>, unsigned int, db::Instance, unsigned int>
{
public:
typedef typename hier_clusters<T>::box_type box_type;
typedef typename local_cluster<T>::id_type id_type;
typedef std::map<ClusterInstance, id_type> connector_map;
struct ClusterInstanceInteraction
{
ClusterInstanceInteraction (size_t _cluster_id, const ClusterInstance &_other_ci, int _soft)
: cluster_id (_cluster_id), other_ci (_other_ci), soft (_soft)
{ }
bool operator== (const ClusterInstanceInteraction &other) const
{
return cluster_id == other.cluster_id && other_ci == other.other_ci && soft == other.soft;
}
size_t cluster_id;
ClusterInstance other_ci;
int soft;
};
/**
* @brief Constructor
*/
hc_receiver (const db::Layout &layout, const db::Cell &cell, db::connected_clusters<T> &cell_clusters, hier_clusters<T> &tree, const cell_clusters_box_converter<T> &cbc, const db::Connectivity &conn, const std::set<db::cell_index_type> *breakout_cells, typename hier_clusters<T>::instance_interaction_cache_type *instance_interaction_cache, bool separate_attributes)
{
mp_layout = &layout;
mp_cell = &cell;
mp_tree = &tree;
mp_cbc = &cbc;
mp_conn = &conn;
mp_breakout_cells = breakout_cells;
m_cluster_cache_misses = 0;
m_cluster_cache_hits = 0;
mp_instance_interaction_cache = instance_interaction_cache;
m_separate_attributes = separate_attributes;
mp_cell_clusters = &cell_clusters;
}
/**
* @brief Gets the cache size
*/
size_t cluster_cache_size () const
{
db::MemStatisticsSimple ms;
ms << m_interaction_cache_for_clusters;
return ms.used ();
}
/**
* @brief Gets the cache hits
*/
size_t cluster_cache_hits () const
{
return m_cluster_cache_hits;
}
/**
* @brief Gets the cache misses
*/
size_t cluster_cache_misses () const
{
return m_cluster_cache_misses;
}
/**
* @brief Receiver main event for instance-to-instance interactions
*/
void add (const db::Instance *i1, unsigned int /*p1*/, const db::Instance *i2, unsigned int /*p2*/)
{
db::ICplxTrans t;
std::list<ClusterInstancePair> ic;
consider_instance_pair (box_type::world (), *i1, t, db::CellInstArray::iterator (), *i2, t, db::CellInstArray::iterator (), ic);
connect_clusters (ic);
}
/**
* @brief Single-instance treatment - may be required because of interactions between array members
*/
void finish (const db::Instance *i, unsigned int /*p1*/)
{
consider_single_inst (*i);
}
/**
* @brief Receiver main event for local-to-instance interactions
*/
void add (const local_cluster<T> *c1, unsigned int /*p1*/, const db::Instance *i2, unsigned int /*p2*/)
{
std::list<ClusterInstanceInteraction> ic;
db::ICplxTrans t;
consider_cluster_instance_pair (*c1, *i2, t, ic);
ic.unique ();
m_ci_interactions.splice (m_ci_interactions.end (), ic, ic.begin (), ic.end ());
}
/**
* @brief Finally join the clusters in the join set
*
* This step is postponed because doing this while the iteration happens would
* invalidate the box trees.
*/
void finish_cluster_to_instance_interactions ()
{
for (typename std::list<ClusterInstanceInteraction>::const_iterator ii = m_ci_interactions.begin (); ii != m_ci_interactions.end (); ++ii) {
// NOTE: soft connections make a dummy cluster at the level of our instance. This allows registering a soft connection to it.
mp_tree->propagate_cluster_inst (*mp_layout, *mp_cell, ii->other_ci, mp_cell->cell_index (), ii->soft != 0 ? true /*with self*/ : false /*without self*/);
}
for (typename std::list<ClusterInstanceInteraction>::const_iterator ii = m_ci_interactions.begin (); ii != m_ci_interactions.end (); ++ii) {
if (ii->soft != 0) {
id_type other = mp_cell_clusters->find_cluster_with_connection (ii->other_ci);
tl_assert (other > 0); // because we used "with self" in "propagate_cluster_inst"
register_soft_connection (ii->cluster_id, other, ii->soft);
} else {
id_type other = mp_cell_clusters->find_cluster_with_connection (ii->other_ci);
if (other > 0) {
// we found a child cluster that connects two clusters on our own level:
// we must join them into one, but not now. We're still iterating and
// would invalidate the box trees. So keep this now and combine the clusters later.
mark_to_join (other, ii->cluster_id);
} else {
mp_cell_clusters->add_connection (ii->cluster_id, ii->other_ci);
}
}
}
for (auto sc = m_soft_connections.begin (); sc != m_soft_connections.end (); ++sc) {
if (sc->second == 0) {
// dropped soft connection
continue;
}
if (sc->second > 0) {
mp_cell_clusters->make_soft_connection (sc->first.second, sc->first.first);
} else {
mp_cell_clusters->make_soft_connection (sc->first.first, sc->first.second);
}
}
for (typename std::list<std::set<id_type> >::const_iterator sc = m_cm2join_sets.begin (); sc != m_cm2join_sets.end (); ++sc) {
if (sc->empty ()) {
// dropped ones are empty
continue;
}
typename std::set<id_type>::const_iterator c = sc->begin ();
typename std::set<id_type>::const_iterator cc = c;
for (++cc; cc != sc->end (); ++cc) {
mp_cell_clusters->join_cluster_with (*c, *cc);
}
}
}
// needs explicit implementation because we have two base classes:
bool stop () const { return false; }
void initialize () { }
void finalize (bool) { }
private:
const db::Layout *mp_layout;
const db::Cell *mp_cell;
db::connected_clusters<T> *mp_cell_clusters;
hier_clusters<T> *mp_tree;
const cell_clusters_box_converter<T> *mp_cbc;
const db::Connectivity *mp_conn;
const std::set<db::cell_index_type> *mp_breakout_cells;
typedef std::list<std::set<id_type> > join_set_list;
std::map<id_type, typename join_set_list::iterator> m_cm2join_map;
join_set_list m_cm2join_sets;
std::map<std::pair<size_t, size_t>, int> m_soft_connections;
std::list<ClusterInstanceInteraction> m_ci_interactions;
instance_interaction_cache<interaction_key_for_clusters<box_type>, std::list<ClusterIDPair> > m_interaction_cache_for_clusters;
size_t m_cluster_cache_misses, m_cluster_cache_hits;
typename hier_clusters<T>::instance_interaction_cache_type *mp_instance_interaction_cache;
bool m_separate_attributes;
/**
* @brief Investigate a pair of instances
*
* @param common The common box of both instances
* @param i1 The first instance to investigate
* @param t1 The parent instances' cumulated transformation
* @param i1element selects a specific instance from i1 (unless == db::CellInstArray::iterator())
* @param i2 The second instance to investigate
* @param t2 The parent instances' cumulated transformation
* @param i2element selects a specific instance from i2 (unless == db::CellInstArray::iterator())
* @param interacting_clusters_out Receives the cluster interaction descriptors
*
* "interacting_clusters_out" will be cluster interactions in the parent instance space of i1 and i2 respectively.
* Cluster ID will be valid in the parent cells containing i1 and i2.
*/
void consider_instance_pair (const box_type &common,
const db::Instance &i1, const db::ICplxTrans &t1, const db::CellInstArray::iterator &i1element,
const db::Instance &i2, const db::ICplxTrans &t2, const db::CellInstArray::iterator &i2element,
cluster_instance_pair_list_type &interacting_clusters)
{
if (is_breakout_cell (mp_breakout_cells, i1.cell_index ()) || is_breakout_cell (mp_breakout_cells, i2.cell_index ())) {
return;
}
box_type bb1 = (*mp_cbc) (i1.cell_index ());
box_type b1 = i1.cell_inst ().bbox (*mp_cbc).transformed (t1);
box_type bb2 = (*mp_cbc) (i2.cell_index ());
box_type b2 = i2.cell_inst ().bbox (*mp_cbc).transformed (t2);
box_type common_all = common & b1 & b2;
if (common_all.empty ()) {
return;
}
// gross shortcut: the cells do no comprise a constellation which will ever interact
if (! mp_conn->interact (mp_layout->cell (i1.cell_index ()), mp_layout->cell (i2.cell_index ()))) {
return;
}
InstanceToInstanceInteraction ii_key;
db::ICplxTrans i1t, i2t;
bool fill_cache = false;
size_t n1 = i1element.at_end () ? i1.size () : 1;
size_t n2 = i2element.at_end () ? i2.size () : 1;
// Cache is only used for single instances or simple and regular arrays.
if ((n1 == 1 || ! i1.is_iterated_array ()) &&
(n2 == 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 cache_norm = tt1.inverted ();
ii_key = InstanceToInstanceInteraction ((! i1element.at_end () || i1.size () == 1) ? 0 : i1.cell_inst ().delegate (),
(! i2element.at_end () || i2.size () == 1) ? 0 : i2.cell_inst ().delegate (),
cache_norm, cache_norm * tt2);
const cluster_instance_pair_list_type *cached = mp_instance_interaction_cache->find (i1.cell_index (), i2.cell_index (), ii_key);
if (cached) {
// use cached interactions
interacting_clusters = *cached;
for (cluster_instance_pair_list_type::iterator i = interacting_clusters.begin (); i != interacting_clusters.end (); ++i) {
// translate the property IDs
i->a.set_inst_prop_id (i1.prop_id ());
i->a.transform (i1t);
i->b.set_inst_prop_id (i2.prop_id ());
i->b.transform (i2t);
}
return;
}
// avoid caching few-to-many interactions as this typically does not contribute anything
fill_cache = true;
}
// shortcut: if the instances cannot interact because their bounding boxes do no comprise a valid constellation,
// reject the pair
bool any = false;
for (db::Connectivity::all_layer_iterator la = mp_conn->begin_layers (); la != mp_conn->end_layers () && ! any; ++la) {
db::box_convert<db::CellInst, true> bca (*mp_layout, *la);
box_type bb1 = i1.cell_inst ().bbox (bca).transformed (t1);
if (! bb1.empty ()) {
db::Connectivity::layer_iterator lbe = mp_conn->end_connected (*la);
for (db::Connectivity::layer_iterator lb = mp_conn->begin_connected (*la); lb != lbe && ! any; ++lb) {
db::box_convert<db::CellInst, true> bcb (*mp_layout, lb->first);
box_type bb2 = i2.cell_inst ().bbox (bcb).transformed (t2);
any = bb1.touches (bb2);
}
}
}
if (! any) {
if (fill_cache) {
mp_instance_interaction_cache->insert (i1.cell_index (), i2.cell_index (), ii_key);
}
return;
}
// array interactions
db::ICplxTrans t1i = t1.inverted ();
db::ICplxTrans t2i = t2.inverted ();
// TODO: optimize for single inst?
for (db::CellInstArray::iterator ii1 = i1element.at_end () ? i1.begin_touching (common_all.transformed (t1i), mp_layout) : i1element; ! ii1.at_end (); ++ii1) {
db::ICplxTrans i1t = i1.complex_trans (*ii1);
db::ICplxTrans tt1 = t1 * i1t;
box_type ib1 = bb1.transformed (tt1);
for (db::CellInstArray::iterator ii2 = i2element.at_end () ? i2.begin_touching (ib1.transformed (t2i), mp_layout) : i2element; ! ii2.at_end (); ++ii2) {
db::ICplxTrans i2t = i2.complex_trans (*ii2);
db::ICplxTrans tt2 = t2 * i2t;
if (i1.cell_index () == i2.cell_index () && tt1 == tt2) {
// skip interactions between identical instances (duplicate instance removal)
if (! i2element.at_end ()) {
break;
} else {
continue;
}
}
box_type ib2 = bb2.transformed (tt2);
box_type common12 = ib1 & ib2 & common;
if (! common12.empty ()) {
const std::list<ClusterIDPair> &i2i_interactions = compute_instance_interactions (common12, i1.cell_index (), tt1, i2.cell_index (), tt2);
for (std::list<ClusterIDPair>::const_iterator ii = i2i_interactions.begin (); ii != i2i_interactions.end (); ++ii) {
ClusterInstance k1 (ii->a, i1.cell_index (), i1t, i1.prop_id ());
ClusterInstance k2 (ii->b, i2.cell_index (), i2t, i2.prop_id ());
interacting_clusters.push_back (ClusterInstancePair (k1, k2, ii->soft));
}
// dive into cell of ii2
const db::Cell &cell2 = mp_layout->cell (i2.cell_index ());
for (db::Cell::touching_iterator jj2 = cell2.begin_touching (common12.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) {
std::list<ClusterInstancePair> ii_interactions;
consider_instance_pair (common12, i1, t1, ii1, *jj2, tt2, db::CellInstArray::iterator (), ii_interactions);
for (std::list<ClusterInstancePair>::iterator i = ii_interactions.begin (); i != ii_interactions.end (); ++i) {
propagate_cluster_inst (i->b, i2.cell_index (), i2t, i2.prop_id ());
}
ii_interactions.unique ();
interacting_clusters.splice (interacting_clusters.end (), ii_interactions, ii_interactions.begin (), ii_interactions.end ());
}
// dive into cell of ii1
const db::Cell &cell1 = mp_layout->cell (i1.cell_index ());
for (db::Cell::touching_iterator jj1 = cell1.begin_touching (common12.transformed (tt1.inverted ())); ! jj1.at_end (); ++jj1) {
std::list<ClusterInstancePair> ii_interactions;
consider_instance_pair (common12, *jj1, tt1, db::CellInstArray::iterator (), i2, t2, ii2, ii_interactions);
for (std::list<ClusterInstancePair>::iterator i = ii_interactions.begin (); i != ii_interactions.end (); ++i) {
propagate_cluster_inst (i->a, i1.cell_index (), i1t, i1.prop_id ());
}
ii_interactions.unique ();
interacting_clusters.splice (interacting_clusters.end (), ii_interactions, ii_interactions.begin (), ii_interactions.end ());
}
}
if (! i2element.at_end ()) {
break;
}
}
if (! i1element.at_end ()) {
break;
}
}
// remove duplicates (after doing a quick unique before)
// NOTE: duplicates may happen due to manifold child/child interactions which boil down to
// identical parent cluster interactions.
std::vector<ClusterInstancePair> sorted_interactions;
sorted_interactions.reserve (interacting_clusters.size ());
sorted_interactions.insert (sorted_interactions.end (), interacting_clusters.begin (), interacting_clusters.end ());
interacting_clusters.clear ();
std::sort (sorted_interactions.begin (), sorted_interactions.end ());
sorted_interactions.erase (std::unique (sorted_interactions.begin (), sorted_interactions.end ()), sorted_interactions.end ());
// return the list of unique interactions
interacting_clusters.insert (interacting_clusters.end (), sorted_interactions.begin (), sorted_interactions.end ());
if (fill_cache && sorted_interactions.size () < instance_to_instance_cache_set_size_threshold) {
// normalize transformations for cache
db::ICplxTrans i1ti = i1t.inverted (), i2ti = i2t.inverted ();
for (std::vector<ClusterInstancePair>::iterator i = sorted_interactions.begin (); i != sorted_interactions.end (); ++i) {
i->a.transform (i1ti);
i->b.transform (i2ti);
}
cluster_instance_pair_list_type &cached = mp_instance_interaction_cache->insert (i1.cell_index (), i2.cell_index (), ii_key);
cached.insert (cached.end (), sorted_interactions.begin (), sorted_interactions.end ());
}
}
/**
* @brief Computes a list of interacting clusters for two instances
*/
const std::list<ClusterIDPair> &
compute_instance_interactions (const box_type &common,
db::cell_index_type ci1, const db::ICplxTrans &t1,
db::cell_index_type ci2, const db::ICplxTrans &t2)
{
if (is_breakout_cell (mp_breakout_cells, ci1) || is_breakout_cell (mp_breakout_cells, ci2)) {
static const std::list<ClusterIDPair> empty;
return empty;
}
db::ICplxTrans t1i = t1.inverted ();
db::ICplxTrans t2i = t2.inverted ();
db::ICplxTrans t21 = t1i * t2;
box_type common2 = common.transformed (t2i);
interaction_key_for_clusters<box_type> ikey (t1i, t21, common2);
const std::list<ClusterIDPair> *ici = m_interaction_cache_for_clusters.find (ci1, ci2, ikey);
if (ici) {
return *ici;
} else {
const db::Cell &cell2 = mp_layout->cell (ci2);
const db::local_clusters<T> &cl1 = mp_tree->clusters_per_cell (ci1);
const db::local_clusters<T> &cl2 = mp_tree->clusters_per_cell (ci2);
std::list<ClusterIDPair> &new_interactions = m_interaction_cache_for_clusters.insert (ci1, ci2, ikey);
db::ICplxTrans t12 = t2i * t1;
for (typename db::local_clusters<T>::touching_iterator i = cl1.begin_touching (common2.transformed (t21)); ! i.at_end (); ++i) {
// skip the test, if this cluster doesn't interact with the whole cell2
if (! i->interacts (cell2, t21, *mp_conn)) {
continue;
}
box_type bc2 = (common2 & i->bbox ().transformed (t12));
for (typename db::local_clusters<T>::touching_iterator j = cl2.begin_touching (bc2); ! j.at_end (); ++j) {
int soft = 0;
if ((! m_separate_attributes || i->same_attrs (*j)) && i->interacts (*j, t21, *mp_conn, soft)) {
new_interactions.push_back (ClusterIDPair (i->id (), j->id (), soft));
}
}
}
return new_interactions;
}
}
/**
* @brief Single instance treatment
*/
void consider_single_inst (const db::Instance &i)
{
if (is_breakout_cell (mp_breakout_cells, i.cell_index ())) {
return;
}
box_type bb = (*mp_cbc) (i.cell_index ());
const db::Cell &cell = mp_layout->cell (i.cell_index ());
for (db::CellInstArray::iterator ii = i.begin (); ! ii.at_end (); ++ii) {
db::ICplxTrans tt = i.complex_trans (*ii);
box_type ib = bb.transformed (tt);
std::vector<ClusterInstElement> pp;
pp.push_back (ClusterInstElement (i.cell_index (), i.complex_trans (*ii), i.prop_id ()));
bool any = false;
for (db::CellInstArray::iterator ii2 = i.begin_touching (ib, mp_layout); ! ii2.at_end (); ++ii2) {
db::ICplxTrans tt2 = i.complex_trans (*ii2);
if (tt.equal (tt2)) {
// skip the initial instance
continue;
}
box_type ib2 = bb.transformed (tt2);
if (ib.touches (ib2)) {
std::vector<ClusterInstElement> pp2;
pp2.push_back (ClusterInstElement (i.cell_index (), i.complex_trans (*ii2), i.prop_id ()));
cluster_instance_pair_list_type interacting_clusters;
box_type common = (ib & ib2);
const std::list<ClusterIDPair> &i2i_interactions = compute_instance_interactions (common, i.cell_index (), tt, i.cell_index (), tt2);
for (std::list<ClusterIDPair>::const_iterator ii = i2i_interactions.begin (); ii != i2i_interactions.end (); ++ii) {
ClusterInstance k1 (ii->a, i.cell_index (), tt, i.prop_id ());
ClusterInstance k2 (ii->b, i.cell_index (), tt2, i.prop_id ());
interacting_clusters.push_back (ClusterInstancePair (k1, k2, ii->soft));
}
for (db::Cell::touching_iterator jj2 = cell.begin_touching (common.transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) {
db::ICplxTrans t;
std::list<ClusterInstancePair> ii_interactions;
consider_instance_pair (common, i, t, ii, *jj2, tt2, db::CellInstArray::iterator (), ii_interactions);
for (std::list<ClusterInstancePair>::iterator ii = ii_interactions.begin (); ii != ii_interactions.end (); ++ii) {
propagate_cluster_inst (ii->b, i.cell_index (), tt2, i.prop_id ());
}
interacting_clusters.splice (interacting_clusters.end (), ii_interactions, ii_interactions.begin (), ii_interactions.end ());
}
connect_clusters (interacting_clusters);
any = true;
}
}
// we don't expect more to happen on the next instance
if (! any) {
break;
}
}
}
/**
* @brief Handles a local clusters vs. the clusters of a specific child instance or instance array
* @param c1 The local cluster
* @param i2 The index of the child cell
* @param t2 The accumulated transformation of the path, not including i2
* @param interactions_out Delivers the interactions
*/
void consider_cluster_instance_pair (const local_cluster<T> &c1, const db::Instance &i2, const db::ICplxTrans &t2, std::list<ClusterInstanceInteraction> &interactions_out)
{
if (is_breakout_cell (mp_breakout_cells, i2.cell_index ())) {
return;
}
box_type bb2 = (*mp_cbc) (i2.cell_index ());
const db::Cell &cell2 = mp_layout->cell (i2.cell_index ());
box_type b1 = c1.bbox ();
box_type b2 = i2.cell_inst ().bbox (*mp_cbc).transformed (t2);
if (! b1.touches (b2)) {
return;
}
std::vector<ClusterIDPair> c2i_interactions;
for (db::CellInstArray::iterator ii2 = i2.begin_touching ((b1 & b2).transformed (t2.inverted ()), mp_layout); ! ii2.at_end (); ++ii2) {
db::ICplxTrans i2t = i2.complex_trans (*ii2);
db::ICplxTrans tt2 = t2 * i2t;
box_type ib2 = bb2.transformed (tt2);
if (b1.touches (ib2) && c1.interacts (cell2, tt2, *mp_conn)) {
c2i_interactions.clear ();
compute_cluster_instance_interactions (c1, i2.cell_index (), tt2, c2i_interactions);
for (std::vector<ClusterIDPair>::const_iterator ii = c2i_interactions.begin (); ii != c2i_interactions.end (); ++ii) {
ClusterInstance k (ii->b, i2.cell_index (), i2t, i2.prop_id ());
interactions_out.push_back (ClusterInstanceInteraction (ii->a, k, ii->soft));
}
// dive into cell of ii2
for (db::Cell::touching_iterator jj2 = cell2.begin_touching ((b1 & ib2).transformed (tt2.inverted ())); ! jj2.at_end (); ++jj2) {
std::list<ClusterInstanceInteraction> ci_interactions;
consider_cluster_instance_pair (c1, *jj2, tt2, ci_interactions);
for (typename std::list<ClusterInstanceInteraction>::iterator ii = ci_interactions.begin (); ii != ci_interactions.end (); ++ii) {
propagate_cluster_inst (ii->other_ci, i2.cell_index (), i2t, i2.prop_id ());
}
interactions_out.splice (interactions_out.end (), ci_interactions, ci_interactions.begin (), ci_interactions.end ());
}
}
}
}
/**
* @brief Handles a local clusters vs. the clusters of a specific child instance
* @param c1 The local cluster
* @param ci2 The cell index of the cell investigated
* @param p2 The instantiation path to the child cell (last element is the instance to ci2)
* @param t2 The accumulated transformation of the path
*/
void
compute_cluster_instance_interactions (const local_cluster<T> &c1,
db::cell_index_type ci2, const db::ICplxTrans &t2,
std::vector<ClusterIDPair> &interactions)
{
if (is_breakout_cell (mp_breakout_cells, ci2)) {
return;
}
const db::local_clusters<T> &cl2 = mp_tree->clusters_per_cell (ci2);
for (typename db::local_clusters<T>::touching_iterator j = cl2.begin_touching (c1.bbox ().transformed (t2.inverted ())); ! j.at_end (); ++j) {
int soft = 0;
if ((! m_separate_attributes || c1.same_attrs (*j)) && c1.interacts (*j, t2, *mp_conn, soft)) {
interactions.push_back (ClusterIDPair (c1.id (), j->id (), soft));
}
}
}
/**
* @brief Inserts a pair of clusters to join
*/
void mark_to_join (id_type a, id_type b)
{
if (a == b) {
// shouldn't happen, but duplicate instances may trigger this
return;
}
typename std::map<id_type, typename join_set_list::iterator>::const_iterator x = m_cm2join_map.find (a);
typename std::map<id_type, typename join_set_list::iterator>::const_iterator y = m_cm2join_map.find (b);
if (x == m_cm2join_map.end ()) {
if (y == m_cm2join_map.end ()) {
m_cm2join_sets.push_back (std::set<id_type> ());
m_cm2join_sets.back ().insert (a);
m_cm2join_sets.back ().insert (b);
m_cm2join_map [a] = --m_cm2join_sets.end ();
m_cm2join_map [b] = --m_cm2join_sets.end ();
} else {
y->second->insert (a);
m_cm2join_map [a] = y->second;
}
} else if (y == m_cm2join_map.end ()) {
x->second->insert (b);
m_cm2join_map [b] = x->second;
} else if (x->second != y->second) {
// join two superclusters
typename join_set_list::iterator yset = y->second;
x->second->insert (yset->begin (), yset->end ());
for (typename std::set<id_type>::const_iterator i = yset->begin (); i != yset->end (); ++i) {
m_cm2join_map [*i] = x->second;
}
m_cm2join_sets.erase (yset);
}
#if defined(DEBUG_HIER_NETWORK_PROCESSOR)
// concistency check for debugging
for (typename std::map<id_type, typename join_set_list::iterator>::const_iterator j = m_cm2join_map.begin (); j != m_cm2join_map.end (); ++j) {
tl_assert (j->second->find (j->first) != j->second->end ());
}
for (typename std::list<std::set<id_type> >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) {
for (typename std::set<id_type>::const_iterator j = i->begin(); j != i->end(); ++j) {
tl_assert(m_cm2join_map.find (*j) != m_cm2join_map.end ());
tl_assert(m_cm2join_map[*j] == i);
}
}
// the sets must be disjunct
std::set<id_type> all;
for (typename std::list<std::set<id_type> >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) {
for (typename std::set<id_type>::const_iterator j = i->begin(); j != i->end(); ++j) {
tl_assert(all.find (*j) == all.end());
all.insert(*j);
}
}
#endif
}
/**
* @brief Makes cluster connections from all parents of a cell into this cell and to the given cluster
*
* After calling this method, the cluster instance in ci is guaranteed to have connections from all
* parent cells. One of these connections represents the instance ci.
*
* Returns false if the connection was already there in the same place (indicating duplicate instances).
* In this case, the cluster instance should be skipped. In the other case, the cluster instance is
* updated to reflect the connected cluster.
*/
void propagate_cluster_inst (ClusterInstance &ci, db::cell_index_type pci, const db::ICplxTrans &trans, db::properties_id_type prop_id) const
{
size_t id_new = mp_tree->propagate_cluster_inst (*mp_layout, *mp_cell, ci, pci, true);
tl_assert (id_new != 0);
ci = db::ClusterInstance (id_new, pci, trans, prop_id);
}
/**
* @brief Establishes connections between the cluster instances listed in the argument
*/
void connect_clusters (const cluster_instance_pair_list_type &interacting_clusters)
{
for (cluster_instance_pair_list_type::const_iterator ic = interacting_clusters.begin (); ic != interacting_clusters.end (); ++ic) {
const ClusterInstance &k1 = ic->a;
const ClusterInstance &k2 = ic->b;
// Note: "with_self" is false as we're going to create a connected cluster anyway
id_type x1 = mp_tree->propagate_cluster_inst (*mp_layout, *mp_cell, k1, mp_cell->cell_index (), false);
id_type x2 = mp_tree->propagate_cluster_inst (*mp_layout, *mp_cell, k2, mp_cell->cell_index (), false);
if (x1 == 0) {
if (x2 == 0) {
if (ic->soft != 0) {
id_type ca = mp_cell_clusters->insert_dummy ();
id_type cb = mp_cell_clusters->insert_dummy ();
mp_cell_clusters->add_connection (ca, k1);
mp_cell_clusters->add_connection (cb, k2);
register_soft_connection (ca, cb, ic->soft);
} else {
id_type connector = mp_cell_clusters->insert_dummy ();
mp_cell_clusters->add_connection (connector, k1);
mp_cell_clusters->add_connection (connector, k2);
}
} else {
if (ic->soft != 0) {
id_type ca = mp_cell_clusters->insert_dummy ();
mp_cell_clusters->add_connection (ca, k1);
register_soft_connection (ca, x2, ic->soft);
} else {
mp_cell_clusters->add_connection (x2, k1);
}
}
} else if (x2 == 0) {
if (ic->soft != 0) {
id_type cb = mp_cell_clusters->insert_dummy ();
mp_cell_clusters->add_connection (cb, k2);
register_soft_connection (x1, cb, ic->soft);
} else {
mp_cell_clusters->add_connection (x1, k2);
}
} else if (x1 != x2) {
int soft = ic->soft;
// for instance-to-instance interactions the number of connections is more important for the
// cost of the join operation: make the one with more connections the target
// TODO: this will be SLOW for STL's not providing a fast size()
if (mp_cell_clusters->connections_for_cluster (x1).size () < mp_cell_clusters->connections_for_cluster (x2).size ()) {
std::swap (x1, x2);
soft = -soft;
}
if (soft != 0) {
register_soft_connection (x1, x2, soft);
} else {
mp_cell_clusters->join_cluster_with (x1, x2);
mp_cell_clusters->remove_cluster (x2);
}
}
}
}
/**
* @brief Registers a soft connection for clusters a and b
*/
void register_soft_connection (id_type a, id_type b, int soft)
{
std::pair<size_t, size_t> key (a < b ? a : b, a < b ? b : a);
if (!(a < b)) {
soft = -soft;
}
auto sc = m_soft_connections.find (key);
if (sc != m_soft_connections.end ()) {
if (sc->second != 0 && sc->second != soft) {
sc->second = 0; // turn into hard connection
mark_to_join (a, b);
}
} else {
m_soft_connections [key] = soft;
}
}
};
template <class T>
struct cell_inst_clusters_box_converter
{
typedef typename local_cluster<T>::box_type box_type;
typedef db::simple_bbox_tag complexity;
cell_inst_clusters_box_converter (const cell_clusters_box_converter<T> &cbc)
: mp_cbc (&cbc)
{
// .. nothing yet ..
}
box_type operator() (const db::Instance &inst) const
{
return inst.cell_inst ().bbox (*mp_cbc);
}
private:
const cell_clusters_box_converter<T> *mp_cbc;
};
}
template <class T>
size_t
hier_clusters<T>::propagate_cluster_inst (const db::Layout &layout, const db::Cell &cell, const ClusterInstance &ci, db::cell_index_type pci, bool with_self)
{
connected_clusters<T> &target_cc = clusters_per_cell (pci);
size_t parent_cluster = target_cc.find_cluster_with_connection (ci);
size_t id_new = 0;
if (parent_cluster > 0) {
// take parent
id_new = parent_cluster;
} else {
size_t id = ci.id ();
// if we're attaching to a child which is root yet, we need to promote the
// cluster to the parent in all places
connected_clusters<T> &child_cc = clusters_per_cell (ci.inst_cell_index ());
if (child_cc.is_root (id)) {
std::set<std::pair<db::cell_index_type, ClusterInstance> > seen; // to avoid duplicate connections
if (! with_self) {
// don't include the instance we came up with initially
seen.insert (std::make_pair (cell.cell_index (), ci));
}
const db::Cell &child_cell = layout.cell (ci.inst_cell_index ());
for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
db::Instance child_inst = pi->child_inst ();
connected_clusters<T> &parent_cc = clusters_per_cell (pi->parent_cell_index ());
for (db::CellInstArray::iterator pii = child_inst.begin (); ! pii.at_end (); ++pii) {
ClusterInstance ci2 (id, child_inst.cell_index (), child_inst.complex_trans (*pii), child_inst.prop_id ());
if (seen.find (std::make_pair (pi->parent_cell_index (), ci2)) == seen.end ()) {
size_t id_dummy;
const typename db::local_cluster<T>::global_nets &gn = child_cc.cluster_by_id (id).get_global_nets ();
if (gn.empty ()) {
id_dummy = parent_cc.insert_dummy ();
} else {
local_cluster<T> *lc = parent_cc.insert ();
lc->set_global_nets (gn);
id_dummy = lc->id ();
}
parent_cc.add_connection (id_dummy, ci2);
seen.insert (std::make_pair (pi->parent_cell_index (), ci2));
if (pci == pi->parent_cell_index () && ci == ci2) {
id_new = id_dummy;
}
}
}
}
child_cc.reset_root (id);
}
}
return id_new;
}
template <class T>
void
hier_clusters<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::map<db::cell_index_type, tl::equivalence_clusters<size_t> > *attr_equivalence, const std::set<db::cell_index_type> *breakout_cells, bool separate_attributes)
{
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity, tl::to_string (tr ("Computing shape clusters")));
std::set<db::cell_index_type> called;
cell.collect_called_cells (called);
called.insert (cell.cell_index ());
// first build all local clusters
{
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::to_string (tr ("Computing local shape clusters")));
tl::RelativeProgress progress (tl::to_string (tr ("Computing local clusters")), called.size (), 1);
for (std::set<db::cell_index_type>::const_iterator c = called.begin (); c != called.end (); ++c) {
// look for the net label joining spec - for the top cell the "top_cell_index" entry is looked for.
// If there is no such entry or the cell is not the top cell, look for the entry by cell index.
std::map<db::cell_index_type, tl::equivalence_clusters<size_t> >::const_iterator ae;
const tl::equivalence_clusters<size_t> *ec = 0;
if (attr_equivalence) {
if (*c == cell.cell_index ()) {
ae = attr_equivalence->find (top_cell_index);
if (ae != attr_equivalence->end ()) {
ec = &ae->second;
}
}
if (! ec) {
ae = attr_equivalence->find (*c);
if (ae != attr_equivalence->end ()) {
ec = &ae->second;
}
}
}
build_local_cluster (layout, layout.cell (*c), conn, ec, separate_attributes);
++progress;
}
}
// build the hierarchical connections bottom-up and for all cells whose children are computed already
instance_interaction_cache_type instance_interaction_cache;
{
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::to_string (tr ("Computing hierarchical shape clusters")));
tl::RelativeProgress progress (tl::to_string (tr ("Computing hierarchical clusters")), called.size (), 1);
std::set<db::cell_index_type> done;
std::vector<db::cell_index_type> todo;
for (db::Layout::bottom_up_const_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) {
if (called.find (*c) != called.end ()) {
bool all_available = true;
const db::Cell &cell = layout.cell (*c);
for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end () && all_available; ++cc) {
all_available = (done.find (*cc) != done.end ());
}
if (all_available) {
todo.push_back (*c);
} else {
tl_assert (! todo.empty ());
build_hier_connections_for_cells (cbc, layout, todo, conn, breakout_cells, progress, instance_interaction_cache, separate_attributes);
done.insert (todo.begin (), todo.end ());
todo.clear ();
todo.push_back (*c);
}
}
}
build_hier_connections_for_cells (cbc, layout, todo, conn, breakout_cells, progress, instance_interaction_cache, separate_attributes);
}
if (tl::verbosity () >= m_base_verbosity + 20) {
tl::info << "Cluster build cache statistics (instance to instance cache): size=" << instance_interaction_cache.size () << ", hits=" << instance_interaction_cache.hits () << ", misses=" << instance_interaction_cache.misses ();
}
}
template <class T>
void
hier_clusters<T>::build_local_cluster (const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const tl::equivalence_clusters<size_t> *attr_equivalence, bool separate_attributes)
{
std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ()));
if (tl::verbosity () >= m_base_verbosity + 20) {
tl::log << msg;
}
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 20, msg);
connected_clusters<T> &local = m_per_cell_clusters [cell.cell_index ()];
local.build_clusters (cell, conn, attr_equivalence, true, separate_attributes);
}
template <class T>
void
hier_clusters<T>::build_hier_connections_for_cells (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const std::vector<db::cell_index_type> &cells, const db::Connectivity &conn, const std::set<db::cell_index_type> *breakout_cells, tl::RelativeProgress &progress, instance_interaction_cache_type &instance_interaction_cache, bool separate_attributes)
{
for (std::vector<db::cell_index_type>::const_iterator c = cells.begin (); c != cells.end (); ++c) {
build_hier_connections (cbc, layout, layout.cell (*c), conn, breakout_cells, instance_interaction_cache, separate_attributes);
++progress;
}
}
namespace {
class GlobalNetClusterMaker
{
public:
typedef std::pair<std::set<size_t>, std::set<ClusterInstance> > entry_type;
typedef std::list<entry_type> entry_list;
typedef entry_list::const_iterator entry_iterator;
void
add (const std::set<size_t> &global_nets, size_t cluster_id)
{
add (global_nets, ClusterInstance (cluster_id));
}
void
add (const std::set<size_t> &global_nets, const ClusterInstance &inst)
{
if (global_nets.empty ()) {
return;
}
std::set<size_t>::const_iterator g0 = global_nets.begin ();
std::map<size_t, entry_list::iterator>::iterator k = m_global_net_to_entries.find (*g0);
if (k == m_global_net_to_entries.end ()) {
m_entries.push_back (entry_type ());
m_entries.back ().first.insert (*g0);
k = m_global_net_to_entries.insert (std::make_pair (*g0, --m_entries.end ())).first;
}
k->second->second.insert (inst);
for (std::set<size_t>::const_iterator g = ++g0; g != global_nets.end (); ++g) {
std::map<size_t, entry_list::iterator>::iterator j = m_global_net_to_entries.find (*g);
if (j == m_global_net_to_entries.end ()) {
k->second->first.insert (*g);
k->second->second.insert (inst);
m_global_net_to_entries.insert (std::make_pair (*g, k->second));
} else if (k->second != j->second) {
// joining required
k->second->first.insert (j->second->first.begin (), j->second->first.end ());
k->second->second.insert (j->second->second.begin (), j->second->second.end ());
m_entries.erase (j->second);
j->second = k->second;
}
}
}
entry_iterator begin () const
{
return m_entries.begin ();
}
entry_iterator end () const
{
return m_entries.end ();
}
private:
entry_list m_entries;
std::map<size_t, entry_list::iterator> m_global_net_to_entries;
};
template <class T>
struct is_for_nets
{
static bool value () { return false; }
};
template <>
struct is_for_nets<db::NetShape>
{
static bool value () { return true; }
};
}
template <class T>
void
hier_clusters<T>::build_hier_connections (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::set<db::cell_index_type> *breakout_cells, instance_interaction_cache_type &instance_interaction_cache, bool separate_attributes)
{
std::string msg = tl::to_string (tr ("Computing hierarchical clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ()));
if (tl::verbosity () >= m_base_verbosity + 20) {
tl::log << msg;
}
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 20, msg);
connected_clusters<T> &local = m_per_cell_clusters [cell.cell_index ()];
// NOTE: this is a receiver for both the child-to-child and
// local to child interactions.
std::unique_ptr<hc_receiver<T> > rec (new hc_receiver<T> (layout, cell, local, *this, cbc, conn, breakout_cells, &instance_interaction_cache, separate_attributes));
cell_inst_clusters_box_converter<T> cibc (cbc);
// The box scanner needs pointers so we have to first store the instances
// delivered by the cell's iterator (which does not deliver real pointer).
std::vector<db::Instance> inst_storage;
// TODO: there should be a cell.size () for this ...
size_t n = 0;
for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) {
n += 1;
}
inst_storage.reserve (n);
for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) {
inst_storage.push_back (*inst);
}
// handle instance to instance connections
{
static std::string desc = tl::to_string (tr ("Instance to instance treatment"));
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 30, desc);
db::box_scanner<db::Instance, unsigned int> bs (true, desc);
for (std::vector<db::Instance>::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) {
if (! is_breakout_cell (breakout_cells, inst->cell_index ())) {
bs.insert (inst.operator-> (), 0);
}
}
bs.process (*rec, 1 /*touching*/, cibc);
}
// handle local to instance connections
{
std::list<local_cluster<T> > heap;
double area_ratio = 10.0;
static std::string desc = tl::to_string (tr ("Local to instance treatment"));
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 30, desc);
db::box_scanner2<db::local_cluster<T>, unsigned int, db::Instance, unsigned int> bs2 (true, desc);
for (typename connected_clusters<T>::const_iterator c = local.begin (); c != local.end (); ++c) {
// we do not actually need the original clusters. For a better performance we optimize the
// area ratio and split, but we keep the ID the same.
std::back_insert_iterator<std::list<local_cluster<T> > > iout = std::back_inserter (heap);
size_t n = c->split (area_ratio, iout);
if (n == 0) {
bs2.insert1 (c.operator-> (), 0);
} else {
typename std::list<local_cluster<T> >::iterator h = heap.end ();
while (n-- > 0) {
bs2.insert1 ((--h).operator-> (), 0);
}
}
}
for (std::vector<db::Instance>::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) {
if (! is_breakout_cell (breakout_cells, inst->cell_index ())) {
bs2.insert2 (inst.operator-> (), 0);
}
}
bs2.process (*rec, 1 /*touching*/, local_cluster_box_convert<T> (), cibc);
}
// join local clusters which got connected by child clusters
rec->finish_cluster_to_instance_interactions ();
if (is_for_nets<T>::value ()) {
// remove empty or point-like clusters which do not make a downward connection - i.e. from stray texts.
// This implies, we cannot connect from upward down to such nets too. In other words: to force a pin,
// inside a cell we need more than a text.
// (issue #1719, part 2)
std::vector<typename local_cluster<T>::id_type> to_delete;
for (typename connected_clusters<T>::const_iterator c = local.begin (); c != local.end (); ++c) {
box_type bbox = c->bbox ();
if ((bbox.empty () || (bbox.width () == 0 && bbox.height () == 0)) && c->get_global_nets ().empty () && local.connections_for_cluster (c->id ()).empty ()) {
to_delete.push_back (c->id ());
}
}
for (auto i = to_delete.begin (); i != to_delete.end (); ++i) {
local.remove_cluster (*i);
}
if (tl::verbosity () >= m_base_verbosity + 20) {
tl::info << "Removed " << to_delete.size () << " clusters because they are point-like or empty and do not have connections downward (stray texts)";
}
}
if (tl::verbosity () >= m_base_verbosity + 20) {
tl::info << "Cluster build cache statistics (instance to shape cache): size=" << rec->cluster_cache_size () << ", hits=" << rec->cluster_cache_hits () << ", misses=" << rec->cluster_cache_misses ();
}
rec.reset (0);
// finally connect global nets
{
static std::string desc = tl::to_string (tr ("Global net treatment"));
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 30, desc);
GlobalNetClusterMaker global_net_clusters;
// insert the global nets from the subcircuits which need connection
for (std::vector<db::Instance>::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) {
const db::connected_clusters<T> &cc = m_per_cell_clusters [inst->cell_index ()];
for (typename db::connected_clusters<T>::const_iterator cl = cc.begin (); cl != cc.end (); ++cl) {
if (! cl->get_global_nets ().empty ()) {
for (db::Instance::cell_inst_array_type::iterator i = inst->begin (); !i.at_end (); ++i) {
global_net_clusters.add (cl->get_global_nets (), db::ClusterInstance (cl->id (), inst->cell_index (), inst->complex_trans (*i), inst->prop_id ()));
}
}
}
}
// insert the global nets from here
for (typename db::connected_clusters<T>::const_iterator cl = local.begin (); cl != local.end (); ++cl) {
const typename db::local_cluster<T>::global_nets &gn = cl->get_global_nets ();
if (! gn.empty ()) {
global_net_clusters.add (gn, db::ClusterInstance (cl->id ()));
}
}
// now global_net_clusters knows what clusters need to be made for the global nets
for (GlobalNetClusterMaker::entry_iterator ge = global_net_clusters.begin (); ge != global_net_clusters.end (); ++ge) {
db::local_cluster<T> *gc = local.insert ();
gc->set_global_nets (ge->first);
// NOTE: don't use the gc pointer - it may become invalid during make_path (will also do a local.insert)
size_t gcid = gc->id ();
for (std::set<ClusterInstance>::const_iterator i = ge->second.begin (); i != ge->second.end (); ++i) {
if (! i->has_instance ()) {
local.join_cluster_with (gcid, i->id ());
} else {
// ensures the cluster is propagated so we can connect it with another
size_t other_id = propagate_cluster_inst (layout, cell, *i, cell.cell_index (), false);
if (other_id == gcid) {
// shouldn't happen, but duplicate instances may trigger this
} else if (other_id) {
local.join_cluster_with (gcid, other_id);
} else {
local.add_connection (gcid, *i);
}
}
}
}
}
}
template <class T>
const connected_clusters<T> &
hier_clusters<T>::clusters_per_cell (db::cell_index_type cell_index) const
{
typename std::map<db::cell_index_type, connected_clusters<T> >::const_iterator c = m_per_cell_clusters.find (cell_index);
if (c == m_per_cell_clusters.end ()) {
static connected_clusters<T> empty;
return empty;
} else {
return c->second;
}
}
template <class T>
connected_clusters<T> &
hier_clusters<T>::clusters_per_cell (db::cell_index_type cell_index)
{
typename std::map<db::cell_index_type, connected_clusters<T> >::iterator c = m_per_cell_clusters.find (cell_index);
if (c == m_per_cell_clusters.end ()) {
c = m_per_cell_clusters.insert (std::make_pair (cell_index, connected_clusters<T> ())).first;
}
return c->second;
}
template <class T>
void
hier_clusters<T>::return_to_hierarchy (db::Layout &layout, const std::map<unsigned int, unsigned int> &lm) const
{
for (db::Layout::bottom_up_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) {
const db::connected_clusters<T> &cc = clusters_per_cell (*c);
db::Cell &target_cell = layout.cell (*c);
for (typename db::connected_clusters<T>::all_iterator lc = cc.begin_all (); ! lc.at_end (); ++lc) {
if (cc.is_root (*lc)) {
for (typename std::map<unsigned int, unsigned int>::const_iterator m = lm.begin (); m != lm.end (); ++m) {
db::Shapes &shapes = target_cell.shapes (m->second);
for (recursive_cluster_shape_iterator<T> si (*this, m->first, *c, *lc); ! si.at_end (); ++si) {
insert_transformed (layout, shapes, *si, si.trans ());
}
}
}
}
}
}
// explicit instantiations
template class DB_PUBLIC hier_clusters<db::NetShape>;
template class DB_PUBLIC hier_clusters<db::PolygonRef>;
template class DB_PUBLIC hier_clusters<db::Edge>;
// ------------------------------------------------------------------------------
// recursive_cluster_shape_iterator implementation
template <class T>
recursive_cluster_shape_iterator<T>::recursive_cluster_shape_iterator (const hier_clusters<T> &hc, unsigned int layer, db::cell_index_type ci, typename local_cluster<T>::id_type id, const CircuitCallback *callback)
: mp_hc (&hc), m_layer (layer), m_id (id), mp_callback (callback)
{
if (id == 0) {
return;
}
down (ci, id, db::ICplxTrans ());
while (m_shape_iter.at_end () && ! m_conn_iter_stack.empty ()) {
next_conn ();
}
}
template <class T>
std::vector<ClusterInstance> recursive_cluster_shape_iterator<T>::inst_path () const
{
std::vector<db::ClusterInstance> p;
if (!m_conn_iter_stack.empty ()) {
p.reserve (m_conn_iter_stack.size ());
for (size_t i = 0; i < m_conn_iter_stack.size () - 1; ++i) {
p.push_back (*m_conn_iter_stack [i].first);
}
}
return p;
}
template <class T>
recursive_cluster_shape_iterator<T> &recursive_cluster_shape_iterator<T>::operator++ ()
{
++m_shape_iter;
while (m_shape_iter.at_end () && ! m_conn_iter_stack.empty ()) {
next_conn ();
}
return *this;
}
template <class T>
void recursive_cluster_shape_iterator<T>::skip_cell ()
{
m_shape_iter = typename db::local_cluster<T>::shape_iterator ();
do {
up ();
if (m_conn_iter_stack.empty ()) {
return;
}
++m_conn_iter_stack.back ().first;
} while (m_conn_iter_stack.back ().first == m_conn_iter_stack.back ().second);
while (m_shape_iter.at_end () && ! m_conn_iter_stack.empty ()) {
next_conn ();
}
}
template <class T>
void recursive_cluster_shape_iterator<T>::next_conn ()
{
if (m_conn_iter_stack.back ().first != m_conn_iter_stack.back ().second) {
const ClusterInstance &cli = *m_conn_iter_stack.back ().first;
if (mp_callback && ! mp_callback->new_cell (cli.inst_cell_index ())) {
// skip this cell
++m_conn_iter_stack.back ().first;
} else {
down (cli.inst_cell_index (), cli.id (), cli.inst_trans ());
}
} else {
while (m_conn_iter_stack.back ().first == m_conn_iter_stack.back ().second) {
up ();
if (m_conn_iter_stack.empty ()) {
return;
}
++m_conn_iter_stack.back ().first;
}
}
}
template <class T>
void recursive_cluster_shape_iterator<T>::up ()
{
m_conn_iter_stack.pop_back ();
m_trans_stack.pop_back ();
m_cell_index_stack.pop_back ();
}
template <class T>
void recursive_cluster_shape_iterator<T>::down (db::cell_index_type ci, typename db::local_cluster<T>::id_type id, const db::ICplxTrans &t)
{
const connected_clusters<T> &clusters = mp_hc->clusters_per_cell (ci);
const typename connected_clusters<T>::connections_type &conn = clusters.connections_for_cluster (id);
if (! m_trans_stack.empty ()) {
m_trans_stack.push_back (m_trans_stack.back () * t);
} else {
m_trans_stack.push_back (t);
}
m_cell_index_stack.push_back (ci);
m_conn_iter_stack.push_back (std::make_pair (conn.begin (), conn.end ()));
const local_cluster<T> &cluster = mp_hc->clusters_per_cell (cell_index ()).cluster_by_id (cluster_id ());
m_shape_iter = cluster.begin (m_layer);
}
// explicit instantiations
template class DB_PUBLIC recursive_cluster_shape_iterator<db::NetShape>;
template class DB_PUBLIC recursive_cluster_shape_iterator<db::PolygonRef>;
template class DB_PUBLIC recursive_cluster_shape_iterator<db::Edge>;
// ------------------------------------------------------------------------------
// recursive_cluster_iterator implementation
template <class T>
recursive_cluster_iterator<T>::recursive_cluster_iterator (const hier_clusters<T> &hc, db::cell_index_type ci, typename local_cluster<T>::id_type id)
: mp_hc (&hc), m_id (id)
{
down (ci, id);
}
template <class T>
std::vector<ClusterInstance> recursive_cluster_iterator<T>::inst_path () const
{
std::vector<db::ClusterInstance> p;
if (!m_conn_iter_stack.empty ()) {
p.reserve (m_conn_iter_stack.size ());
for (size_t i = 0; i < m_conn_iter_stack.size () - 1; ++i) {
p.push_back (*m_conn_iter_stack [i].first);
}
}
return p;
}
template <class T>
recursive_cluster_iterator<T> &recursive_cluster_iterator<T>::operator++ ()
{
next_conn ();
return *this;
}
template <class T>
void recursive_cluster_iterator<T>::next_conn ()
{
while (m_conn_iter_stack.back ().first == m_conn_iter_stack.back ().second) {
up ();
if (m_conn_iter_stack.empty ()) {
return;
}
++m_conn_iter_stack.back ().first;
}
if (m_conn_iter_stack.back ().first != m_conn_iter_stack.back ().second) {
const ClusterInstance &cli = *m_conn_iter_stack.back ().first;
down (cli.inst_cell_index (), cli.id ());
}
}
template <class T>
void recursive_cluster_iterator<T>::up ()
{
m_conn_iter_stack.pop_back ();
m_cell_index_stack.pop_back ();
}
template <class T>
void recursive_cluster_iterator<T>::down (db::cell_index_type ci, typename db::local_cluster<T>::id_type id)
{
const connected_clusters<T> &clusters = mp_hc->clusters_per_cell (ci);
const typename connected_clusters<T>::connections_type &conn = clusters.connections_for_cluster (id);
m_cell_index_stack.push_back (ci);
m_conn_iter_stack.push_back (std::make_pair (conn.begin (), conn.end ()));
}
// explicit instantiations
template class DB_PUBLIC recursive_cluster_iterator<db::NetShape>;
template class DB_PUBLIC recursive_cluster_iterator<db::PolygonRef>;
template class DB_PUBLIC recursive_cluster_iterator<db::Edge>;
// ------------------------------------------------------------------------------
// incoming_cluster_connections implementation
template <class T>
incoming_cluster_connections<T>::incoming_cluster_connections (const db::Layout &layout, const db::Cell &cell, const hier_clusters<T> &hc)
: mp_layout (const_cast<db::Layout *> (&layout)), mp_hc (const_cast<hier_clusters<T> *> (&hc))
{
cell.collect_called_cells (m_called_cells);
m_called_cells.insert (cell.cell_index ());
}
template <class T>
bool
incoming_cluster_connections<T>::has_incoming (db::cell_index_type ci, size_t cluster_id) const
{
std::map<db::cell_index_type, std::map<size_t, incoming_connections> >::const_iterator i = m_incoming.find (ci);
if (i == m_incoming.end ()) {
ensure_computed (ci);
i = m_incoming.find (ci);
tl_assert (i != m_incoming.end ());
}
tl_assert (i != m_incoming.end ());
return (i->second.find (cluster_id) != i->second.end ());
}
template <class T>
const typename incoming_cluster_connections<T>::incoming_connections &
incoming_cluster_connections<T>::incoming (db::cell_index_type ci, size_t cluster_id) const
{
std::map<db::cell_index_type, std::map<size_t, incoming_connections> >::const_iterator i = m_incoming.find (ci);
if (i == m_incoming.end ()) {
ensure_computed (ci);
i = m_incoming.find (ci);
tl_assert (i != m_incoming.end ());
}
std::map<size_t, incoming_connections>::const_iterator ii = i->second.find (cluster_id);
if (ii != i->second.end ()) {
return ii->second;
} else {
static incoming_connections empty;
return empty;
}
}
template <class T>
void
incoming_cluster_connections<T>::ensure_computed (db::cell_index_type ci) const
{
tl_assert (mp_layout.get () != 0);
m_incoming.insert (std::make_pair (ci, std::map<size_t, incoming_connections> ()));
const db::Cell &cell = mp_layout->cell (ci);
for (db::Cell::parent_cell_iterator pc = cell.begin_parent_cells (); pc != cell.end_parent_cells (); ++pc) {
if (m_called_cells.find (*pc) != m_called_cells.end ()) {
ensure_computed_parent (*pc);
}
}
m_called_cells.erase (ci);
}
template <class T>
void
incoming_cluster_connections<T>::ensure_computed_parent (db::cell_index_type ci) const
{
ensure_computed (ci);
const connected_clusters<T> &cc = ((const hier_clusters<T> *) mp_hc.get ())->clusters_per_cell (ci);
for (typename connected_clusters<T>::connections_iterator x = cc.begin_connections (); x != cc.end_connections (); ++x) {
for (typename connected_clusters<T>::connections_type::const_iterator xx = x->second.begin (); xx != x->second.end (); ++xx) {
m_incoming [xx->inst_cell_index ()][xx->id ()].push_back (IncomingClusterInstance (ci, x->first, *xx));
}
}
}
// explicit instantiations
template class DB_PUBLIC incoming_cluster_connections<db::NetShape>;
template class DB_PUBLIC incoming_cluster_connections<db::PolygonRef>;
template class DB_PUBLIC incoming_cluster_connections<db::Edge>;
}