mirror of https://github.com/KLayout/klayout.git
3187 lines
85 KiB
C++
3187 lines
85 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 "dbLayout.h"
|
|
#include "dbMemStatistics.h"
|
|
#include "dbTrans.h"
|
|
#include "dbTechnology.h"
|
|
#include "dbShapeRepository.h"
|
|
#include "dbPCellHeader.h"
|
|
#include "dbPCellVariant.h"
|
|
#include "dbPCellDeclaration.h"
|
|
#include "dbLibraryProxy.h"
|
|
#include "dbColdProxy.h"
|
|
#include "dbLibraryManager.h"
|
|
#include "dbLibrary.h"
|
|
#include "dbCellVariants.h"
|
|
#include "dbRegion.h"
|
|
#include "dbEdgePairs.h"
|
|
#include "dbEdges.h"
|
|
#include "dbTexts.h"
|
|
#include "dbCellMapping.h"
|
|
#include "dbLayerMapping.h"
|
|
#include "dbLayoutUtils.h"
|
|
#include "dbCellVariants.h"
|
|
#include "tlTimer.h"
|
|
#include "tlLog.h"
|
|
#include "tlInternational.h"
|
|
#include "tlProgress.h"
|
|
#include "tlAssert.h"
|
|
|
|
|
|
namespace db
|
|
{
|
|
|
|
static const int layout_base_verbosity = 30;
|
|
|
|
// -----------------------------------------------------------------
|
|
// The undo/redo operations
|
|
|
|
struct LayoutOp
|
|
: public db::Op
|
|
{
|
|
LayoutOp () { }
|
|
virtual ~LayoutOp () { }
|
|
|
|
virtual void redo (db::Layout *) const = 0;
|
|
virtual void undo (db::Layout *) const = 0;
|
|
};
|
|
|
|
struct SetLayoutPropId
|
|
: public LayoutOp
|
|
{
|
|
SetLayoutPropId (db::properties_id_type f, db::properties_id_type t)
|
|
: m_from (f), m_to (t)
|
|
{ }
|
|
|
|
virtual void redo (db::Layout *layout) const
|
|
{
|
|
layout->prop_id (m_to);
|
|
}
|
|
|
|
virtual void undo (db::Layout *layout) const
|
|
{
|
|
layout->prop_id (m_from);
|
|
}
|
|
|
|
private:
|
|
db::properties_id_type m_from, m_to;
|
|
};
|
|
|
|
struct SetLayoutTechName
|
|
: public LayoutOp
|
|
{
|
|
SetLayoutTechName (const std::string &from, const std::string &to)
|
|
: m_from (from), m_to (to)
|
|
{ }
|
|
|
|
virtual void redo (db::Layout *layout) const
|
|
{
|
|
layout->set_technology_name_without_update (m_to);
|
|
}
|
|
|
|
virtual void undo (db::Layout *layout) const
|
|
{
|
|
layout->set_technology_name_without_update (m_from);
|
|
}
|
|
|
|
private:
|
|
std::string m_from, m_to;
|
|
};
|
|
|
|
struct SetLayoutDBU
|
|
: public LayoutOp
|
|
{
|
|
SetLayoutDBU (double f, double t)
|
|
: m_from (f), m_to (t)
|
|
{ }
|
|
|
|
virtual void redo (db::Layout *layout) const
|
|
{
|
|
layout->dbu (m_to);
|
|
}
|
|
|
|
virtual void undo (db::Layout *layout) const
|
|
{
|
|
layout->dbu (m_from);
|
|
}
|
|
|
|
private:
|
|
double m_from, m_to;
|
|
};
|
|
|
|
struct RenameCellOp
|
|
: public LayoutOp
|
|
{
|
|
RenameCellOp (db::cell_index_type i, const std::string &f, const std::string &t)
|
|
: m_cell_index (i), m_from (f), m_to (t)
|
|
{ }
|
|
|
|
virtual void redo (db::Layout *layout) const
|
|
{
|
|
layout->rename_cell (m_cell_index, m_to.c_str ());
|
|
}
|
|
|
|
virtual void undo (db::Layout *layout) const
|
|
{
|
|
layout->rename_cell (m_cell_index, m_from.c_str ());
|
|
}
|
|
|
|
private:
|
|
db::cell_index_type m_cell_index;
|
|
std::string m_from, m_to;
|
|
};
|
|
|
|
struct NewRemoveCellOp
|
|
: public LayoutOp
|
|
{
|
|
NewRemoveCellOp (db::cell_index_type i, const std::string &name, bool remove, db::Cell *cell)
|
|
: m_cell_index (i), m_name (name), m_remove (remove), mp_cell (cell)
|
|
{ }
|
|
|
|
~NewRemoveCellOp ()
|
|
{
|
|
if (mp_cell) {
|
|
delete mp_cell;
|
|
}
|
|
}
|
|
|
|
virtual void redo (db::Layout *layout) const
|
|
{
|
|
if (m_remove) {
|
|
remove_cell (layout);
|
|
} else {
|
|
new_cell (layout);
|
|
}
|
|
}
|
|
|
|
virtual void undo (db::Layout *layout) const
|
|
{
|
|
if (m_remove) {
|
|
new_cell (layout);
|
|
} else {
|
|
remove_cell (layout);
|
|
}
|
|
}
|
|
|
|
private:
|
|
db::cell_index_type m_cell_index;
|
|
std::string m_name;
|
|
bool m_remove;
|
|
mutable db::Cell *mp_cell;
|
|
|
|
virtual void new_cell (db::Layout *layout) const
|
|
{
|
|
tl_assert (mp_cell != 0);
|
|
layout->insert_cell (m_cell_index, m_name, mp_cell);
|
|
mp_cell = 0; // now it belongs to the layout
|
|
}
|
|
|
|
virtual void remove_cell (db::Layout *layout) const
|
|
{
|
|
tl_assert (mp_cell == 0);
|
|
mp_cell = layout->take_cell (m_cell_index);
|
|
}
|
|
};
|
|
|
|
struct SetLayerPropertiesOp
|
|
: public LayoutOp
|
|
{
|
|
SetLayerPropertiesOp (unsigned int l, const db::LayerProperties &new_props, const db::LayerProperties &old_props)
|
|
: m_layer_index (l), m_new_props (new_props), m_old_props (old_props)
|
|
{ }
|
|
|
|
virtual void redo (db::Layout *layout) const
|
|
{
|
|
layout->set_properties (m_layer_index, m_new_props);
|
|
}
|
|
|
|
virtual void undo (db::Layout *layout) const
|
|
{
|
|
layout->set_properties (m_layer_index, m_old_props);
|
|
}
|
|
|
|
private:
|
|
unsigned int m_layer_index;
|
|
db::LayerProperties m_new_props, m_old_props;
|
|
};
|
|
|
|
struct InsertRemoveLayerOp
|
|
: public LayoutOp
|
|
{
|
|
InsertRemoveLayerOp (unsigned int l, const db::LayerProperties &props, bool insert)
|
|
: m_layer_index (l), m_props (props), m_insert (insert)
|
|
{ }
|
|
|
|
virtual void redo (db::Layout *layout) const
|
|
{
|
|
if (m_insert) {
|
|
layout->insert_layer (m_layer_index, m_props);
|
|
} else {
|
|
layout->delete_layer (m_layer_index);
|
|
}
|
|
}
|
|
|
|
virtual void undo (db::Layout *layout) const
|
|
{
|
|
if (! m_insert) {
|
|
layout->insert_layer (m_layer_index, m_props);
|
|
} else {
|
|
layout->delete_layer (m_layer_index);
|
|
}
|
|
}
|
|
|
|
private:
|
|
unsigned int m_layer_index;
|
|
db::LayerProperties m_props;
|
|
bool m_insert;
|
|
};
|
|
|
|
struct SetLayoutMetaInfoOp
|
|
: public LayoutOp
|
|
{
|
|
SetLayoutMetaInfoOp (db::Layout::meta_info_name_id_type name_id, const db::MetaInfo *f, const db::MetaInfo *t)
|
|
: m_name_id (name_id), m_has_from (f != 0), m_has_to (t != 0)
|
|
{
|
|
if (f) {
|
|
m_from = *f;
|
|
}
|
|
if (t) {
|
|
m_to = *t;
|
|
}
|
|
}
|
|
|
|
virtual void redo (db::Layout *layout) const
|
|
{
|
|
if (! m_has_to) {
|
|
layout->remove_meta_info (m_name_id);
|
|
} else {
|
|
layout->add_meta_info (m_name_id, m_to);
|
|
}
|
|
}
|
|
|
|
virtual void undo (db::Layout *layout) const
|
|
{
|
|
if (! m_has_from) {
|
|
layout->remove_meta_info (m_name_id);
|
|
} else {
|
|
layout->add_meta_info (m_name_id, m_from);
|
|
}
|
|
}
|
|
|
|
private:
|
|
db::Layout::meta_info_name_id_type m_name_id;
|
|
bool m_has_from, m_has_to;
|
|
db::MetaInfo m_from, m_to;
|
|
};
|
|
|
|
struct SetCellMetaInfoOp
|
|
: public LayoutOp
|
|
{
|
|
SetCellMetaInfoOp (db::cell_index_type ci, db::Layout::meta_info_name_id_type name_id, const db::MetaInfo *f, const db::MetaInfo *t)
|
|
: m_ci (ci), m_name_id (name_id), m_has_from (f != 0), m_has_to (t != 0)
|
|
{
|
|
if (f) {
|
|
m_from = *f;
|
|
}
|
|
if (t) {
|
|
m_to = *t;
|
|
}
|
|
}
|
|
|
|
virtual void redo (db::Layout *layout) const
|
|
{
|
|
if (! m_has_to) {
|
|
layout->remove_meta_info (m_ci, m_name_id);
|
|
} else {
|
|
layout->add_meta_info (m_ci, m_name_id, m_to);
|
|
}
|
|
}
|
|
|
|
virtual void undo (db::Layout *layout) const
|
|
{
|
|
if (! m_has_from) {
|
|
layout->remove_meta_info (m_ci, m_name_id);
|
|
} else {
|
|
layout->add_meta_info (m_ci, m_name_id, m_from);
|
|
}
|
|
}
|
|
|
|
private:
|
|
db::cell_index_type m_ci;
|
|
db::Layout::meta_info_name_id_type m_name_id;
|
|
bool m_has_from, m_has_to;
|
|
db::MetaInfo m_from, m_to;
|
|
};
|
|
|
|
// -----------------------------------------------------------------
|
|
// Implementation of the ProxyContextInfo class
|
|
|
|
LayoutOrCellContextInfo
|
|
LayoutOrCellContextInfo::deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to)
|
|
{
|
|
LayoutOrCellContextInfo info;
|
|
|
|
for (auto i = from; i != to; ++i) {
|
|
|
|
tl::Extractor ex (i->c_str ());
|
|
|
|
if (ex.test ("LIB=")) {
|
|
|
|
info.lib_name = ex.skip ();
|
|
|
|
} else if (ex.test ("P(")) {
|
|
|
|
std::pair<std::string, tl::Variant> vv;
|
|
|
|
ex.read_word_or_quoted (vv.first);
|
|
ex.test (")");
|
|
ex.test ("=");
|
|
ex.read (vv.second);
|
|
|
|
info.pcell_parameters.insert (vv);
|
|
|
|
} else if (ex.test ("PCELL=")) {
|
|
|
|
info.pcell_name = ex.skip ();
|
|
|
|
} else if (ex.test ("CELL=")) {
|
|
|
|
info.cell_name = ex.skip ();
|
|
|
|
} else if (ex.test ("META(")) {
|
|
|
|
std::pair<std::string, std::pair<tl::Variant, std::string> > vv;
|
|
|
|
ex.read_word_or_quoted (vv.first);
|
|
if (ex.test (",")) {
|
|
ex.read_word_or_quoted (vv.second.second);
|
|
}
|
|
ex.test (")");
|
|
ex.test ("=");
|
|
ex.read (vv.second.first);
|
|
|
|
info.meta_info.insert(vv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
void
|
|
LayoutOrCellContextInfo::serialize (std::vector<std::string> &strings)
|
|
{
|
|
if (! lib_name.empty ()) {
|
|
strings.push_back ("LIB=" + lib_name);
|
|
}
|
|
for (auto p = pcell_parameters.begin (); p != pcell_parameters.end (); ++p) {
|
|
strings.push_back ("P(" + tl::to_word_or_quoted_string (p->first) + ")=" + p->second.to_parsable_string ());
|
|
}
|
|
if (! pcell_name.empty ()) {
|
|
strings.push_back ("PCELL=" + pcell_name);
|
|
}
|
|
if (! cell_name.empty ()) {
|
|
strings.push_back ("CELL=" + cell_name);
|
|
}
|
|
|
|
std::string mv;
|
|
for (auto m = meta_info.begin (); m != meta_info.end (); ++m) {
|
|
mv.clear ();
|
|
mv += "META(";
|
|
mv += tl::to_word_or_quoted_string (m->first);
|
|
if (! m->second.second.empty ()) {
|
|
mv += ",";
|
|
mv += tl::to_word_or_quoted_string (m->second.second);
|
|
}
|
|
mv += ")=";
|
|
mv += m->second.first.to_parsable_string ();
|
|
strings.push_back (mv);
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayoutOrCellContextInfo::has_proxy_info () const
|
|
{
|
|
return !pcell_name.empty () || !lib_name.empty ();
|
|
}
|
|
|
|
bool
|
|
LayoutOrCellContextInfo::has_meta_info () const
|
|
{
|
|
return !meta_info.empty ();
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
// Implementation of the Layout class
|
|
|
|
Layout::Layout (db::Manager *manager)
|
|
: db::Object (manager),
|
|
mp_library (0),
|
|
mp_builder (0),
|
|
m_cells_size (0),
|
|
m_invalid (0),
|
|
m_top_cells (0),
|
|
m_dbu (0.001),
|
|
m_prop_id (0),
|
|
m_properties_repository (this),
|
|
m_do_cleanup (false),
|
|
m_editable (db::default_editable_mode ())
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
Layout::Layout (bool editable, db::Manager *manager)
|
|
: db::Object (manager),
|
|
mp_library (0),
|
|
mp_builder (0),
|
|
m_cells_size (0),
|
|
m_invalid (0),
|
|
m_top_cells (0),
|
|
m_dbu (0.001),
|
|
m_prop_id (0),
|
|
m_properties_repository (this),
|
|
m_do_cleanup (false),
|
|
m_editable (editable)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
Layout::Layout (const db::Layout &layout)
|
|
: db::Object (layout),
|
|
db::LayoutStateModel (),
|
|
gsi::ObjectBase (),
|
|
tl::Object (),
|
|
tl::UniqueId (),
|
|
mp_library (0),
|
|
mp_builder (0),
|
|
m_cells_size (0),
|
|
m_invalid (0),
|
|
m_top_cells (0),
|
|
m_dbu (0.001),
|
|
m_prop_id (0),
|
|
m_properties_repository (this),
|
|
m_do_cleanup (false),
|
|
m_editable (layout.m_editable)
|
|
{
|
|
*this = layout;
|
|
}
|
|
|
|
Layout::~Layout ()
|
|
{
|
|
// since it the cell graph (or the derived layout) might produce some transactions that refer to
|
|
// this object, we need to clear the manager's transaction list before the cell graph is deleted.
|
|
if (manager ()) {
|
|
manager ()->clear ();
|
|
}
|
|
|
|
clear ();
|
|
}
|
|
|
|
void
|
|
Layout::dbu (double d)
|
|
{
|
|
if (fabs (d - m_dbu)) {
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new SetLayoutDBU (m_dbu, d));
|
|
}
|
|
m_dbu = d;
|
|
dbu_changed ();
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::clear ()
|
|
{
|
|
invalidate_hier ();
|
|
|
|
m_free_cell_indices.clear ();
|
|
m_cells.clear ();
|
|
m_cells_size = 0;
|
|
m_cell_ptrs.clear ();
|
|
|
|
m_top_down_list.clear ();
|
|
|
|
m_layers.clear ();
|
|
|
|
for (std::vector<const char *>::const_iterator p = m_cell_names.begin (); p != m_cell_names.end (); ++p) {
|
|
if (*p) {
|
|
delete [] *p;
|
|
}
|
|
}
|
|
m_cell_names.clear ();
|
|
m_cell_map.clear ();
|
|
|
|
m_shape_repository = db::GenericRepository ();
|
|
db::PropertiesRepository empty_pr (this);
|
|
m_properties_repository = empty_pr;
|
|
m_array_repository = db::ArrayRepository ();
|
|
|
|
for (std::vector<pcell_header_type *>::const_iterator pc = m_pcells.begin (); pc != m_pcells.end (); ++pc) {
|
|
delete *pc;
|
|
}
|
|
m_pcells.clear ();
|
|
m_pcell_ids.clear ();
|
|
|
|
m_lib_proxy_map.clear ();
|
|
m_meta_info.clear ();
|
|
}
|
|
|
|
Layout &
|
|
Layout::operator= (const Layout &d)
|
|
{
|
|
if (&d != this) {
|
|
|
|
db::LayoutStateModel::operator= (d);
|
|
|
|
clear ();
|
|
|
|
m_layers = d.m_layers;
|
|
|
|
m_editable = d.m_editable;
|
|
|
|
m_pcell_ids = d.m_pcell_ids;
|
|
m_pcells.reserve (d.m_pcells.size ());
|
|
|
|
for (std::vector<pcell_header_type *>::const_iterator pc = d.m_pcells.begin (); pc != d.m_pcells.end (); ++pc) {
|
|
if (*pc) {
|
|
m_pcells.push_back (new pcell_header_type (**pc));
|
|
} else {
|
|
m_pcells.push_back (0);
|
|
}
|
|
}
|
|
|
|
m_lib_proxy_map = d.m_lib_proxy_map;
|
|
|
|
m_cell_ptrs.resize (d.m_cell_ptrs.size (), 0);
|
|
|
|
for (const_iterator c = d.begin (); c != d.end (); ++c) {
|
|
cell_type *new_cell = (*c).clone (*this);
|
|
m_cells.push_back_ptr (new_cell);
|
|
++m_cells_size;
|
|
m_cell_ptrs [new_cell->cell_index ()] = new_cell;
|
|
}
|
|
|
|
m_properties_repository = d.m_properties_repository; // because the cell assign operator does not map property ID's ..
|
|
m_top_down_list = d.m_top_down_list;
|
|
m_top_cells = d.m_top_cells;
|
|
|
|
m_cell_names.reserve (d.m_cell_names.size ());
|
|
|
|
cell_index_type i = 0;
|
|
for (std::vector<const char *>::const_iterator p = d.m_cell_names.begin (); p != d.m_cell_names.end (); ++p) {
|
|
if (*p) {
|
|
char *pp = new char [strlen (*p) + 1];
|
|
strcpy (pp, *p);
|
|
m_cell_names.push_back (pp);
|
|
m_cell_map.insert (std::make_pair (pp, i));
|
|
} else {
|
|
m_cell_names.push_back (0);
|
|
}
|
|
++i;
|
|
}
|
|
|
|
m_dbu = d.m_dbu;
|
|
|
|
m_meta_info = d.m_meta_info;
|
|
m_meta_info_by_cell = d.m_meta_info_by_cell;
|
|
m_meta_info_names = d.m_meta_info_names;
|
|
m_meta_info_name_map = d.m_meta_info_name_map;
|
|
|
|
m_tech_name = d.m_tech_name;
|
|
|
|
m_prop_id = d.m_prop_id;
|
|
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
const db::Technology *
|
|
Layout::technology () const
|
|
{
|
|
return db::Technologies::instance ()->has_technology (m_tech_name) ? db::Technologies::instance ()->technology_by_name (m_tech_name) : 0;
|
|
}
|
|
|
|
void
|
|
Layout::set_technology_name_without_update (const std::string &tech)
|
|
{
|
|
if (tech != m_tech_name) {
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new SetLayoutTechName (m_tech_name, tech));
|
|
}
|
|
m_tech_name = tech;
|
|
technology_changed_event ();
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::set_technology_name (const std::string &tech)
|
|
{
|
|
if (tech == m_tech_name) {
|
|
return;
|
|
}
|
|
|
|
// determine which library to map to what
|
|
std::map<db::lib_id_type, db::lib_id_type> mapping;
|
|
std::set<db::lib_id_type> seen;
|
|
std::set<db::lib_id_type> lost;
|
|
|
|
for (db::Layout::iterator c = begin (); c != end (); ++c) {
|
|
|
|
db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (&*c);
|
|
if (lib_proxy && seen.find (lib_proxy->lib_id ()) == seen.end ()) {
|
|
|
|
seen.insert (lib_proxy->lib_id ());
|
|
|
|
std::pair<bool, db::lib_id_type> new_id (false, 0);
|
|
const db::Library *l = db::LibraryManager::instance ().lib (lib_proxy->lib_id ());
|
|
if (l) {
|
|
new_id = db::LibraryManager::instance ().lib_by_name (l->get_name (), tech);
|
|
}
|
|
|
|
if (new_id.first && new_id.second != l->get_id ()) {
|
|
mapping.insert (std::make_pair (l->get_id (), new_id.second));
|
|
} else if (! new_id.first) {
|
|
lost.insert (lib_proxy->lib_id ());
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! mapping.empty () || ! lost.empty ()) {
|
|
|
|
bool needs_cleanup = false;
|
|
|
|
std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> > pcells_to_map;
|
|
std::vector<db::LibraryProxy *> lib_cells_to_map;
|
|
std::vector<db::LibraryProxy *> lib_cells_lost;
|
|
|
|
for (db::Layout::iterator c = begin (); c != end (); ++c) {
|
|
|
|
std::map<db::lib_id_type, db::lib_id_type>::const_iterator m;
|
|
|
|
db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (&*c);
|
|
if (! lib_proxy) {
|
|
continue;
|
|
}
|
|
|
|
if ((m = mapping.find (lib_proxy->lib_id ())) != mapping.end ()) {
|
|
|
|
db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ());
|
|
db::Cell *lib_cell = &lib->layout ().cell (lib_proxy->library_cell_index ());
|
|
db::PCellVariant *lib_pcell = dynamic_cast <db::PCellVariant *> (lib_cell);
|
|
if (lib_pcell) {
|
|
pcells_to_map.push_back (std::make_pair (lib_proxy, lib_pcell));
|
|
} else {
|
|
lib_cells_to_map.push_back (lib_proxy);
|
|
}
|
|
|
|
needs_cleanup = true;
|
|
|
|
} else if (lost.find (lib_proxy->lib_id ()) != lost.end ()) {
|
|
|
|
lib_cells_lost.push_back (lib_proxy);
|
|
|
|
needs_cleanup = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// We do PCell resolution before the library proxy resolution. The reason is that
|
|
// PCells may generate library proxies in their instantiation. Hence we must instantiate
|
|
// the PCells before we can resolve them.
|
|
for (std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> >::const_iterator lp = pcells_to_map.begin (); lp != pcells_to_map.end (); ++lp) {
|
|
|
|
db::cell_index_type ci = lp->first->Cell::cell_index ();
|
|
db::PCellVariant *lib_pcell = lp->second;
|
|
|
|
std::pair<bool, pcell_id_type> pn = lib_pcell->layout ()->pcell_by_name (lp->first->get_basic_name ().c_str ());
|
|
|
|
if (! pn.first) {
|
|
|
|
// substitute by a cold proxy
|
|
db::LayoutOrCellContextInfo info;
|
|
get_context_info (ci, info);
|
|
create_cold_proxy_as (info, ci);
|
|
|
|
} else {
|
|
|
|
db::Library *new_lib = db::LibraryManager::instance ().lib (mapping [lp->first->lib_id ()]);
|
|
|
|
const db::PCellDeclaration *old_pcell_decl = lib_pcell->layout ()->pcell_declaration (lib_pcell->pcell_id ());
|
|
const db::PCellDeclaration *new_pcell_decl = new_lib->layout ().pcell_declaration (pn.second);
|
|
if (! old_pcell_decl || ! new_pcell_decl) {
|
|
|
|
// substitute by a cold proxy
|
|
db::LayoutOrCellContextInfo info;
|
|
get_context_info (ci, info);
|
|
create_cold_proxy_as (info, ci);
|
|
|
|
} else {
|
|
|
|
// map pcell parameters by name
|
|
std::map<std::string, tl::Variant> param_by_name = lib_pcell->parameters_by_name ();
|
|
lp->first->remap (new_lib->get_id (), new_lib->layout ().get_pcell_variant (pn.second, new_pcell_decl->map_parameters (param_by_name)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (std::vector<db::LibraryProxy *>::const_iterator lp = lib_cells_to_map.begin (); lp != lib_cells_to_map.end (); ++lp) {
|
|
|
|
db::Library *new_lib = db::LibraryManager::instance ().lib (mapping [(*lp)->lib_id ()]);
|
|
|
|
db::cell_index_type ci = (*lp)->Cell::cell_index ();
|
|
|
|
std::pair<bool, cell_index_type> cn = new_lib->layout ().cell_by_name ((*lp)->get_basic_name ().c_str ());
|
|
|
|
if (! cn.first) {
|
|
|
|
// unlink this proxy: substitute by a cold proxy
|
|
db::LayoutOrCellContextInfo info;
|
|
get_context_info (ci, info);
|
|
create_cold_proxy_as (info, ci);
|
|
|
|
} else {
|
|
|
|
(*lp)->remap (new_lib->get_id (), cn.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (std::vector<db::LibraryProxy *>::const_iterator lp = lib_cells_lost.begin (); lp != lib_cells_lost.end (); ++lp) {
|
|
|
|
db::cell_index_type ci = (*lp)->Cell::cell_index ();
|
|
|
|
// substitute by a cold proxy
|
|
db::LayoutOrCellContextInfo info;
|
|
get_context_info (ci, info);
|
|
create_cold_proxy_as (info, ci);
|
|
|
|
}
|
|
|
|
if (needs_cleanup) {
|
|
cleanup ();
|
|
}
|
|
|
|
}
|
|
|
|
set_technology_name_without_update (tech);
|
|
|
|
// we may have re-established a connection for pending ("cold") proxies so we can try to restore them
|
|
restore_proxies ();
|
|
}
|
|
|
|
void
|
|
Layout::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);
|
|
}
|
|
|
|
m_layers.mem_stat (stat, purpose, cat, true, (void *) this);
|
|
|
|
db::mem_stat (stat, purpose, cat, m_cell_ptrs, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_free_cell_indices, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_top_down_list, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_cell_names, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_cell_map, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_pcells, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_pcell_ids, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_lib_proxy_map, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_meta_info, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_string_repository, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_shape_repository, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_properties_repository, true, (void *) this);
|
|
db::mem_stat (stat, purpose, cat, m_array_repository, true, (void *) this);
|
|
|
|
for (std::vector<const char *>::const_iterator i = m_cell_names.begin (); i != m_cell_names.end (); ++i) {
|
|
stat->add (typeid (char []), (void *) *i, *i ? (strlen (*i) + 1) : 0, *i ? (strlen (*i) + 1) : 0, (void *) this, purpose, cat);
|
|
}
|
|
for (cell_list::const_iterator i = m_cells.begin (); i != m_cells.end (); ++i) {
|
|
db::mem_stat (stat, MemStatistics::CellInfo, int (i->id ()), *i, false, (void *) this);
|
|
}
|
|
for (std::vector<pcell_header_type *>::const_iterator i = m_pcells.begin (); i != m_pcells.end (); ++i) {
|
|
db::mem_stat (stat, MemStatistics::CellInfo, 0, **i, false, (void *) this);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::prop_id (db::properties_id_type id)
|
|
{
|
|
if (m_prop_id != id) {
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new SetLayoutPropId (m_prop_id, id));
|
|
}
|
|
m_prop_id = id;
|
|
}
|
|
}
|
|
|
|
bool
|
|
Layout::has_cell (const char *name)
|
|
{
|
|
return m_cell_map.find (name) != m_cell_map.end ();
|
|
}
|
|
|
|
std::pair<bool, cell_index_type>
|
|
Layout::cell_by_name (const char *name) const
|
|
{
|
|
cell_map_type::const_iterator c = m_cell_map.find (name);
|
|
if (c != m_cell_map.end ()) {
|
|
return std::make_pair (true, c->second);
|
|
} else {
|
|
return std::make_pair (false, 0);
|
|
}
|
|
}
|
|
|
|
const char *
|
|
Layout::cell_name (cell_index_type index) const
|
|
{
|
|
tl_assert (index < m_cell_names.size ());
|
|
return m_cell_names [index];
|
|
}
|
|
|
|
void
|
|
Layout::delete_cells (const std::set<cell_index_type> &cells_to_delete)
|
|
{
|
|
// Collect parent cells
|
|
std::set <cell_index_type> pcs;
|
|
for (std::set<cell_index_type>::const_iterator c = cells_to_delete.begin (); c != cells_to_delete.end (); ++c) {
|
|
const db::Cell &cref = cell (*c);
|
|
for (db::Cell::parent_cell_iterator pc = cref.begin_parent_cells (); pc != cref.end_parent_cells (); ++pc) {
|
|
pcs.insert (*pc);
|
|
}
|
|
}
|
|
|
|
db::LayoutLocker locker (this);
|
|
|
|
// Clear all instances
|
|
for (std::set<cell_index_type>::const_iterator c = cells_to_delete.begin (); c != cells_to_delete.end (); ++c) {
|
|
|
|
db::Cell &cref = cell (*c);
|
|
|
|
cref.clear_insts ();
|
|
|
|
// If transacting, do not use clear_shapes here. This will delete all the shapes containers will
|
|
// will disable us saving undo data with reference to them.
|
|
if (manager () && manager ()->transacting ()) {
|
|
for (unsigned int i = 0; i < layers (); ++i) {
|
|
if (is_valid_layer (i) || is_special_layer (i)) {
|
|
cref.clear (i);
|
|
}
|
|
}
|
|
} else {
|
|
cref.clear_shapes ();
|
|
}
|
|
|
|
}
|
|
|
|
// delete all instances of this cell
|
|
|
|
std::vector <db::Instance> insts_to_delete;
|
|
for (std::set <cell_index_type>::const_iterator pc = pcs.begin (); pc != pcs.end (); ++pc) {
|
|
|
|
db::Cell &parent_cref = cell (*pc);
|
|
|
|
insts_to_delete.clear ();
|
|
for (db::Cell::const_iterator pci = parent_cref.begin (); ! pci.at_end (); ++pci) {
|
|
if (cells_to_delete.find (pci->cell_index ()) != cells_to_delete.end ()) {
|
|
insts_to_delete.push_back (*pci);
|
|
}
|
|
}
|
|
|
|
std::sort (insts_to_delete.begin (), insts_to_delete.end ());
|
|
|
|
parent_cref.erase_insts (insts_to_delete);
|
|
|
|
}
|
|
|
|
// erase the cells themselves
|
|
// If transacting, the cell is not deleted yet. Instead, the transaction object acts as
|
|
// a backup container for the cell. This is necessary since the ID's within manager are given to
|
|
// cell child objects that must remain.
|
|
for (std::set<cell_index_type>::const_iterator c = cells_to_delete.begin (); c != cells_to_delete.end (); ++c) {
|
|
|
|
// supports undo
|
|
clear_meta (*c);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
|
|
// note the "take" method - this takes out the cell
|
|
std::string cn (cell_name (*c));
|
|
manager ()->queue (this, new NewRemoveCellOp (*c, cn, true /*remove*/, take_cell (*c)));
|
|
|
|
} else {
|
|
|
|
// remove the cell - we use take_cell and delete to avoid recursion issues
|
|
delete take_cell (*c);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::delete_cell (cell_index_type id)
|
|
{
|
|
db::Cell &cref = cell (id);
|
|
|
|
std::vector <cell_index_type> pcs;
|
|
for (db::Cell::parent_cell_iterator pc = cref.begin_parent_cells (); pc != cref.end_parent_cells (); ++pc) {
|
|
pcs.push_back (*pc);
|
|
}
|
|
|
|
cref.clear_insts ();
|
|
|
|
// If transacting, do not use clear_shapes here. This will delete all the shapes containers will
|
|
// will disable us saving undo data with reference to them.
|
|
if (manager () && manager ()->transacting ()) {
|
|
for (unsigned int i = 0; i < layers (); ++i) {
|
|
if (is_valid_layer (i) || is_special_layer (i)) {
|
|
cref.clear (i);
|
|
}
|
|
}
|
|
} else {
|
|
cref.clear_shapes ();
|
|
}
|
|
|
|
// delete all instances of this cell
|
|
|
|
std::vector <db::Instance> insts_to_delete;
|
|
for (std::vector <cell_index_type>::const_iterator pc = pcs.begin (); pc != pcs.end (); ++pc) {
|
|
|
|
if (is_valid_cell_index (*pc)) {
|
|
|
|
db::Cell &parent_cref = cell (*pc);
|
|
|
|
insts_to_delete.clear ();
|
|
for (db::Cell::const_iterator pci = parent_cref.begin (); ! pci.at_end (); ++pci) {
|
|
if (pci->cell_index () == id) {
|
|
insts_to_delete.push_back (*pci);
|
|
}
|
|
}
|
|
|
|
std::sort (insts_to_delete.begin (), insts_to_delete.end ());
|
|
|
|
parent_cref.erase_insts (insts_to_delete);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// erase the cell itself
|
|
// If transacting, the cell is not deleted yet. Instead, the transaction object acts as
|
|
// a backup container for the cell. This is necessary since the ID's within manager are given to
|
|
// cell child objects that must remain.
|
|
|
|
// supports undo
|
|
clear_meta (id);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
|
|
// note the "take" method - this takes out the cell
|
|
std::string cn (cell_name (id));
|
|
manager ()->queue (this, new NewRemoveCellOp (id, cn, true /*remove*/, take_cell (id)));
|
|
|
|
} else {
|
|
|
|
// remove the cell - we use take_cell and delete to avoid recursion issues
|
|
delete take_cell (id);
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::insert (db::cell_index_type cell, int layer, const db::Region ®ion)
|
|
{
|
|
region.insert_into (this, cell, layer);
|
|
}
|
|
|
|
void
|
|
Layout::insert (db::cell_index_type cell, int layer, const db::Edges &edges)
|
|
{
|
|
edges.insert_into (this, cell, layer);
|
|
}
|
|
|
|
void
|
|
Layout::insert (db::cell_index_type cell, int layer, const db::EdgePairs &edge_pairs)
|
|
{
|
|
edge_pairs.insert_into (this, cell, layer);
|
|
}
|
|
|
|
void
|
|
Layout::insert (db::cell_index_type cell, int layer, const db::Texts &texts)
|
|
{
|
|
texts.insert_into (this, cell, layer);
|
|
}
|
|
|
|
void
|
|
Layout::flatten (const db::Cell &source_cell, db::Cell &target_cell, const db::ICplxTrans &t, int levels)
|
|
{
|
|
db::ICplxTrans tt = t;
|
|
|
|
if (&source_cell != &target_cell) {
|
|
|
|
unsigned int nlayers = layers ();
|
|
for (unsigned int l = 0; l < nlayers; ++l) {
|
|
|
|
if (is_valid_layer (l)) {
|
|
|
|
db::Shapes &target_shapes = target_cell.shapes (l);
|
|
const db::Shapes &source_shapes = source_cell.shapes (l);
|
|
|
|
tl::ident_map<db::Layout::properties_id_type> pm1;
|
|
for (db::Shapes::shape_iterator sh = source_shapes.begin (db::ShapeIterator::All); ! sh.at_end (); ++sh) {
|
|
target_shapes.insert (*sh, tt, pm1);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (levels == 0) {
|
|
|
|
if (&source_cell != &target_cell) {
|
|
for (db::Cell::const_iterator inst = source_cell.begin (); ! inst.at_end (); ++inst) {
|
|
db::Instance new_inst = target_cell.insert (*inst);
|
|
target_cell.transform (new_inst, tt);
|
|
}
|
|
}
|
|
|
|
} else if (&target_cell == &source_cell) {
|
|
|
|
update ();
|
|
|
|
try {
|
|
|
|
// Note: suppressing the update speeds up the flatten process considerably since
|
|
// even an iteration of the instances requires an update.
|
|
start_changes ();
|
|
|
|
db::Instances old_instances (&target_cell);
|
|
old_instances = target_cell.instances ();
|
|
target_cell.clear_insts ();
|
|
|
|
for (db::Cell::const_iterator inst = old_instances.begin (); ! inst.at_end (); ++inst) {
|
|
|
|
db::CellInstArray cell_inst = inst->cell_inst ();
|
|
|
|
for (db::CellInstArray::iterator a = cell_inst.begin (); ! a.at_end (); ++a) {
|
|
db::ICplxTrans tinst = t * cell_inst.complex_trans (*a);
|
|
flatten (cell (cell_inst.object ().cell_index ()), target_cell, tinst, levels < 0 ? levels : levels - 1);
|
|
}
|
|
|
|
}
|
|
|
|
end_changes ();
|
|
|
|
} catch (...) {
|
|
end_changes ();
|
|
throw;
|
|
}
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
// Note: suppressing the update speeds up the flatten process considerably since
|
|
// even an iteration of the instances requires an update.
|
|
start_changes ();
|
|
|
|
for (db::Cell::const_iterator inst = source_cell.begin (); ! inst.at_end (); ++inst) {
|
|
|
|
db::CellInstArray cell_inst = inst->cell_inst ();
|
|
|
|
for (db::CellInstArray::iterator a = cell_inst.begin (); ! a.at_end (); ++a) {
|
|
db::ICplxTrans tinst = t * cell_inst.complex_trans (*a);
|
|
flatten (cell (cell_inst.object ().cell_index ()), target_cell, tinst, levels < 0 ? levels : levels - 1);
|
|
}
|
|
|
|
}
|
|
|
|
end_changes ();
|
|
|
|
} catch (...) {
|
|
end_changes ();
|
|
throw;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
Layout::flatten (db::Cell &cell_to_flatten, int levels, bool prune)
|
|
{
|
|
std::set<db::cell_index_type> direct_children;
|
|
if (prune) {
|
|
// save direct children
|
|
cell_to_flatten.collect_called_cells (direct_children, 1);
|
|
}
|
|
|
|
flatten (cell_to_flatten, cell_to_flatten, db::ICplxTrans (), levels);
|
|
|
|
if (prune) {
|
|
|
|
// determine all direct children that are orphans now.
|
|
for (std::set<db::cell_index_type>::iterator dc = direct_children.begin (); dc != direct_children.end (); ) {
|
|
std::set<db::cell_index_type>::iterator dc_next = dc;
|
|
++dc_next;
|
|
if (cell (*dc).parent_cells () != 0) {
|
|
direct_children.erase (dc);
|
|
}
|
|
dc = dc_next;
|
|
}
|
|
|
|
// and prune them
|
|
prune_cells (direct_children.begin (), direct_children.end (), levels - 1);
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::prune_cell (cell_index_type id, int levels)
|
|
{
|
|
do_prune_cell_or_subcell (id, levels, false);
|
|
}
|
|
|
|
void
|
|
Layout::prune_subcells (cell_index_type id, int levels)
|
|
{
|
|
do_prune_cell_or_subcell (id, levels, true);
|
|
}
|
|
|
|
void
|
|
Layout::do_prune_cell_or_subcell (cell_index_type id, int levels, bool subcells)
|
|
{
|
|
db::Cell &cref = cell (id);
|
|
|
|
// collect the called cells
|
|
std::set <cell_index_type> called;
|
|
cref.collect_called_cells (called, levels);
|
|
if (! subcells) {
|
|
called.insert (id);
|
|
}
|
|
|
|
// From these cells erase all cells that have parents outside the subtree of our cell.
|
|
// Make sure this is done recursively by doing this top-down.
|
|
for (top_down_iterator c = begin_top_down (); c != end_top_down (); ++c) {
|
|
if (*c != id && called.find (*c) != called.end ()) {
|
|
db::Cell &ccref = cell (*c);
|
|
for (db::Cell::parent_cell_iterator pc = ccref.begin_parent_cells (); pc != ccref.end_parent_cells (); ++pc) {
|
|
if (*pc != id && called.find (*pc) == called.end ()) {
|
|
// we have a parent outside the subset considered currently (either the cell was never in or
|
|
// it was removed itself already): remove this cell from the set of valid subcells.
|
|
called.erase (*c);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// and delete the cells
|
|
delete_cells (called);
|
|
|
|
// erase all instances in the subcells case (because, by definition we don't have any more instances)
|
|
if (subcells) {
|
|
cref.clear_insts ();
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::prune_cells (const std::set<cell_index_type> &ids, int levels)
|
|
{
|
|
do_prune_cells_or_subcells (ids, levels, false);
|
|
}
|
|
|
|
void
|
|
Layout::prune_subcells (const std::set<cell_index_type> &ids, int levels)
|
|
{
|
|
do_prune_cells_or_subcells (ids, levels, true);
|
|
}
|
|
|
|
void
|
|
Layout::do_prune_cells_or_subcells (const std::set<cell_index_type> &ids, int levels, bool subcells)
|
|
{
|
|
// collect the called cells
|
|
std::set <cell_index_type> called;
|
|
for (std::set<cell_index_type>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
|
|
db::Cell &cref = cell (*id);
|
|
cref.collect_called_cells (called, levels);
|
|
}
|
|
called.insert (ids.begin (), ids.end ());
|
|
|
|
// From these cells erase all cells that have parents outside the subtree of our cell.
|
|
// Make sure this is done recursively by doing this top-down.
|
|
for (top_down_iterator c = begin_top_down (); c != end_top_down (); ++c) {
|
|
if (called.find (*c) != called.end () && ids.find (*c) == ids.end ()) {
|
|
db::Cell &ccref = cell (*c);
|
|
for (db::Cell::parent_cell_iterator pc = ccref.begin_parent_cells (); pc != ccref.end_parent_cells (); ++pc) {
|
|
if (called.find (*pc) == called.end ()) {
|
|
// we have a parent outside the subset considered currently (either the cell was never in or
|
|
// it was removed itself already): remove this cell from the set of valid subcells.
|
|
called.erase (*c);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// order the called cells bottom-up
|
|
std::vector <cell_index_type> cells_to_delete;
|
|
cells_to_delete.reserve (called.size ());
|
|
for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) {
|
|
if (called.find (*c) != called.end () && (!subcells || ids.find (*c) == ids.end ())) {
|
|
cells_to_delete.push_back (*c);
|
|
}
|
|
}
|
|
|
|
// and delete these cells
|
|
delete_cells (cells_to_delete.begin (), cells_to_delete.end ());
|
|
|
|
// erase all instances in the subcells case (because, by definition we don't have any more instances)
|
|
if (subcells) {
|
|
for (std::set<cell_index_type>::const_iterator id = ids.begin (); id != ids.end (); ++id) {
|
|
db::Cell &cref = cell (*id);
|
|
cref.clear_insts ();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::delete_cell_rec (cell_index_type id)
|
|
{
|
|
db::Cell &cref = cell (id);
|
|
|
|
// collect the called cells
|
|
std::set <cell_index_type> called;
|
|
cref.collect_called_cells (called);
|
|
called.insert (id);
|
|
|
|
// order the called cells bottom-up
|
|
std::vector <cell_index_type> cells_to_delete;
|
|
cells_to_delete.reserve (called.size ());
|
|
for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) {
|
|
if (called.find (*c) != called.end ()) {
|
|
cells_to_delete.push_back (*c);
|
|
}
|
|
}
|
|
|
|
// and delete these cells
|
|
delete_cells (cells_to_delete.begin (), cells_to_delete.end ());
|
|
}
|
|
|
|
void
|
|
Layout::insert_cell (cell_index_type ci, const std::string &name, db::Cell *cell)
|
|
{
|
|
// this method is supposed to restore a cell deleted before
|
|
tl_assert (m_cell_names.size () > ci);
|
|
tl_assert (m_cell_names [ci] == 0);
|
|
|
|
char *cp = new char [name.size () + 1];
|
|
m_cell_names [ci] = cp;
|
|
strcpy (cp, name.c_str ());
|
|
|
|
invalidate_hier ();
|
|
|
|
m_cells.push_back_ptr (cell);
|
|
m_cell_ptrs [ci] = cell;
|
|
m_cell_map.insert (std::make_pair (cp, ci));
|
|
|
|
cell->reregister ();
|
|
++m_cells_size;
|
|
}
|
|
|
|
db::Cell *
|
|
Layout::take_cell (cell_index_type ci)
|
|
{
|
|
tl_assert (m_cell_ptrs [ci] != 0);
|
|
|
|
invalidate_hier ();
|
|
|
|
cell_type *cell = m_cells.take (iterator (m_cell_ptrs [ci]));
|
|
cell->unregister ();
|
|
--m_cells_size;
|
|
|
|
m_cell_ptrs [ci] = 0;
|
|
|
|
auto mi = m_meta_info_by_cell.find (ci);
|
|
if (mi != m_meta_info_by_cell.end ()) {
|
|
m_meta_info_by_cell.erase (mi);
|
|
}
|
|
|
|
// Using free cell indices does have one significant drawback:
|
|
// The cellview references cannot be uniquely classified as being invalid - because the
|
|
// ID might be reused. This causes problems, when a cell is being deleted and subsequently a
|
|
// cell is being created - a crash occurs. Therefore the free index feature is disabled.
|
|
// If this causes memory consumption problems, it should be considered to use a map and
|
|
// an arbitrary ID.
|
|
// m_free_cell_indices.push_back (ci);
|
|
|
|
if (m_cell_names [ci] != 0) {
|
|
|
|
cell_map_type::iterator cm = m_cell_map.find (m_cell_names [ci]);
|
|
if (cm != m_cell_map.end ()) {
|
|
m_cell_map.erase (cm);
|
|
}
|
|
|
|
delete [] m_cell_names [ci];
|
|
m_cell_names [ci] = 0;
|
|
|
|
}
|
|
|
|
return cell;
|
|
}
|
|
|
|
std::string
|
|
Layout::uniquify_cell_name (const char *name) const
|
|
{
|
|
if (name != 0 && m_cell_map.find (name) == m_cell_map.end ()) {
|
|
return std::string (name);
|
|
} else {
|
|
|
|
std::string b;
|
|
|
|
// if the cell does not have a valid name yet, create a unique one.
|
|
unsigned int j = 0;
|
|
for (unsigned int m = 0x40000000; m > 0; m >>= 1) {
|
|
j += m;
|
|
b = std::string (name ? name : "") + "$" + tl::to_string (j);
|
|
if (m_cell_map.find (b.c_str ()) == m_cell_map.end ()) {
|
|
j -= m;
|
|
}
|
|
}
|
|
|
|
b = std::string (name ? name : "") + "$" + tl::to_string (j + 1);
|
|
return b;
|
|
|
|
}
|
|
}
|
|
|
|
cell_index_type
|
|
Layout::add_cell (const db::Layout &other, db::cell_index_type ci)
|
|
{
|
|
cell_index_type ci_new = add_cell (other.cell_name (ci));
|
|
cell (ci_new).set_ghost_cell (other.cell (ci).is_ghost_cell ());
|
|
|
|
if (&other == this) {
|
|
add_meta_info (ci_new, other.begin_meta (ci), other.end_meta (ci));
|
|
} else {
|
|
for (auto m = other.begin_meta (ci); m != other.end_meta (ci); ++m) {
|
|
add_meta_info (ci_new, meta_info_name_id (other.meta_info_name (m->first)), m->second);
|
|
}
|
|
}
|
|
|
|
return ci_new;
|
|
}
|
|
|
|
cell_index_type
|
|
Layout::add_cell (const char *name)
|
|
{
|
|
std::string b;
|
|
|
|
if (name == 0) {
|
|
|
|
// 0 name means: create a new one
|
|
b = uniquify_cell_name (0);
|
|
name = b.c_str ();
|
|
|
|
} else {
|
|
|
|
cell_map_type::const_iterator cm = m_cell_map.find (name);
|
|
if (cm != m_cell_map.end ()) {
|
|
|
|
const db::Cell &c= cell (cm->second);
|
|
if (c.is_ghost_cell () && c.empty ()) {
|
|
// ghost cells are available as new cells - the idea is to
|
|
// treat them as non-existing.
|
|
return cm->second;
|
|
} else {
|
|
// create a unique name
|
|
b = uniquify_cell_name (name);
|
|
name = b.c_str ();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// create a new cell
|
|
cell_index_type new_index = allocate_new_cell ();
|
|
|
|
cell_type *new_cell = new cell_type (new_index, *this);
|
|
m_cells.push_back_ptr (new_cell);
|
|
m_cell_ptrs [new_index] = new_cell;
|
|
|
|
// enter its index and cell_name
|
|
register_cell_name (name, new_index);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
|
|
}
|
|
|
|
return new_index;
|
|
}
|
|
|
|
cell_index_type
|
|
Layout::add_anonymous_cell ()
|
|
{
|
|
std::string b;
|
|
|
|
// create a new cell
|
|
cell_index_type new_index = allocate_new_cell ();
|
|
|
|
cell_type *new_cell = new cell_type (new_index, *this);
|
|
m_cells.push_back_ptr (new_cell);
|
|
m_cell_ptrs [new_index] = new_cell;
|
|
|
|
// enter its index and cell_name
|
|
register_cell_name (0, new_index);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
|
|
}
|
|
|
|
return new_index;
|
|
}
|
|
|
|
void
|
|
Layout::register_cell_name (const char *name, cell_index_type ci)
|
|
{
|
|
// enter its index and cell_name
|
|
char *cp;
|
|
|
|
if (name == 0) {
|
|
cp = new char [1];
|
|
*cp = 0;
|
|
} else {
|
|
cp = new char [strlen (name) + 1];
|
|
strcpy (cp, name);
|
|
}
|
|
|
|
while (m_cell_names.size () < ci) {
|
|
char *e = new char [1];
|
|
*e = 0;
|
|
m_cell_names.push_back (e);
|
|
}
|
|
|
|
if (m_cell_names.size () > ci) {
|
|
delete [] m_cell_names [ci];
|
|
m_cell_names [ci] = cp;
|
|
} else {
|
|
m_cell_names.push_back (cp);
|
|
}
|
|
|
|
if (name) {
|
|
m_cell_map.insert (std::make_pair (cp, ci));
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::rename_cell (cell_index_type id, const char *name)
|
|
{
|
|
tl_assert (id < m_cell_names.size ());
|
|
|
|
if (strcmp (m_cell_names [id], name) != 0) {
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new RenameCellOp (id, m_cell_names [id], name));
|
|
}
|
|
|
|
m_cell_map.erase (m_cell_names [id]);
|
|
|
|
char *cp = new char [strlen (name) + 1];
|
|
strcpy (cp, name);
|
|
|
|
delete [] m_cell_names [id];
|
|
m_cell_names [id] = cp;
|
|
|
|
m_cell_map.insert (std::make_pair (cp, id));
|
|
|
|
// to enforce a redraw and a rebuild
|
|
cell_name_changed ();
|
|
|
|
}
|
|
}
|
|
|
|
bool
|
|
Layout::topological_sort ()
|
|
{
|
|
m_top_cells = 0;
|
|
m_top_down_list.clear ();
|
|
|
|
// NOTE: we explicitly count the cells here and do not rely on "m_cell_size".
|
|
// Reason is that this is somewhat safer, specifically directly after take() when
|
|
// the cell list is already reduced, but the cell pointers are still containing the cell
|
|
// (issue #905)
|
|
size_t ncells = 0;
|
|
for (const_iterator c = begin (); c != end (); ++c) {
|
|
++ncells;
|
|
}
|
|
m_top_down_list.reserve (ncells);
|
|
|
|
std::vector<size_t> num_parents (m_cell_ptrs.size (), 0);
|
|
|
|
// while there are cells to treat ..
|
|
while (m_top_down_list.size () != ncells) {
|
|
|
|
size_t n_top_down_cells = m_top_down_list.size ();
|
|
|
|
// Treat all cells that do not have all parents reported.
|
|
// For all such a cells, disable the parent counting,
|
|
// add the cell's index to the top-down sorted list and
|
|
// increment the reported parent instance count in all the
|
|
// child cells.
|
|
|
|
for (const_iterator c = begin (); c != end (); ++c) {
|
|
if (c->parent_cells () == num_parents [c->cell_index ()]) {
|
|
m_top_down_list.push_back (c->cell_index ());
|
|
num_parents [c->cell_index ()] = std::numeric_limits<cell_index_type>::max ();
|
|
}
|
|
}
|
|
|
|
// For all these a cells, increment the reported parent instance
|
|
// count in all the child cells.
|
|
for (cell_index_vector::const_iterator ii = m_top_down_list.begin () + n_top_down_cells; ii != m_top_down_list.end (); ++ii) {
|
|
for (cell_type::child_cell_iterator cc = cell (*ii).begin_child_cells (); ! cc.at_end (); ++cc) {
|
|
tl_assert (num_parents [*cc] != std::numeric_limits<cell_index_type>::max ());
|
|
num_parents [*cc] += 1;
|
|
}
|
|
}
|
|
|
|
// If no new cells have been reported this is basically a
|
|
// sign of recursion in the graph.
|
|
if (n_top_down_cells == m_top_down_list.size ()) {
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
// Determine the number of top cells
|
|
for (top_down_iterator e = m_top_down_list.begin (); e != m_top_down_list.end () && cell (*e).is_top (); ++e) {
|
|
++m_top_cells;
|
|
}
|
|
|
|
// The cell graph is fine.
|
|
return true;
|
|
|
|
}
|
|
|
|
void
|
|
Layout::copy_tree_shapes (const db::Layout &source_layout, const db::CellMapping &cm)
|
|
{
|
|
if (this == &source_layout) {
|
|
throw tl::Exception (tl::to_string (tr ("Cannot copy shapes within the same layout")));
|
|
}
|
|
|
|
db::ICplxTrans trans (source_layout.dbu () / dbu ());
|
|
|
|
db::LayerMapping lm;
|
|
lm.create_full (*this, source_layout);
|
|
|
|
db::copy_shapes (*this, source_layout, trans, cm.source_cells (), cm.table (), lm.table ());
|
|
}
|
|
|
|
void
|
|
Layout::copy_tree_shapes (const db::Layout &source_layout, const db::CellMapping &cm, const db::LayerMapping &lm)
|
|
{
|
|
if (this == &source_layout) {
|
|
throw tl::Exception (tl::to_string (tr ("Cannot copy shapes within the same layout")));
|
|
}
|
|
|
|
db::ICplxTrans trans (source_layout.dbu () / dbu ());
|
|
|
|
db::copy_shapes (*this, source_layout, trans, cm.source_cells (), cm.table (), lm.table ());
|
|
}
|
|
|
|
void
|
|
Layout::move_tree_shapes (db::Layout &source_layout, const db::CellMapping &cm)
|
|
{
|
|
if (this == &source_layout) {
|
|
throw tl::Exception (tl::to_string (tr ("Cannot copy shapes within the same layout")));
|
|
}
|
|
|
|
db::ICplxTrans trans (source_layout.dbu () / dbu ());
|
|
|
|
db::LayerMapping lm;
|
|
lm.create_full (*this, source_layout);
|
|
|
|
db::move_shapes (*this, source_layout, trans, cm.source_cells (), cm.table (), lm.table ());
|
|
}
|
|
|
|
void
|
|
Layout::move_tree_shapes (db::Layout &source_layout, const db::CellMapping &cm, const db::LayerMapping &lm)
|
|
{
|
|
if (this == &source_layout) {
|
|
throw tl::Exception (tl::to_string (tr ("Cannot copy shapes within the same layout")));
|
|
}
|
|
|
|
db::ICplxTrans trans (source_layout.dbu () / dbu ());
|
|
|
|
db::move_shapes (*this, source_layout, trans, cm.source_cells (), cm.table (), lm.table ());
|
|
}
|
|
|
|
bool
|
|
Layout::is_valid_cell_index (cell_index_type ci) const
|
|
{
|
|
return ci < m_cell_ptrs.size () && m_cell_ptrs [ci] != 0;
|
|
}
|
|
|
|
cell_index_type
|
|
Layout::allocate_new_cell ()
|
|
{
|
|
invalidate_hier ();
|
|
|
|
cell_index_type new_index;
|
|
if (m_free_cell_indices.empty ()) {
|
|
new_index = cell_index_type (m_cell_ptrs.size ());
|
|
m_cell_ptrs.push_back (0);
|
|
} else {
|
|
new_index = m_free_cell_indices.back ();
|
|
m_free_cell_indices.pop_back ();
|
|
}
|
|
|
|
++m_cells_size;
|
|
|
|
return new_index;
|
|
}
|
|
|
|
void
|
|
Layout::refresh ()
|
|
{
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
c->update ();
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::cleanup (const std::set<db::cell_index_type> &keep)
|
|
{
|
|
// only managed layouts will receive cleanup requests. Never library container layouts - these
|
|
// cannot know if their proxies are not referenced by other proxies.
|
|
if (! m_do_cleanup) {
|
|
return;
|
|
}
|
|
|
|
// deleting cells may create new top cells which need to be deleted as well, hence we iterate
|
|
// until there are no more cells to delete
|
|
while (true) {
|
|
|
|
// delete all cells that are top cells and are proxies. Those cells are proxies no longer required.
|
|
std::set<cell_index_type> cells_to_delete;
|
|
for (top_down_iterator c = begin_top_down (); c != end_top_cells (); ++c) {
|
|
if (cell (*c).is_proxy ()) {
|
|
cells_to_delete.insert (*c);
|
|
}
|
|
}
|
|
|
|
for (std::set<db::cell_index_type>::const_iterator k = keep.begin (); k != keep.end (); ++k) {
|
|
cells_to_delete.erase (*k);
|
|
}
|
|
|
|
if (cells_to_delete.empty ()) {
|
|
break;
|
|
}
|
|
|
|
delete_cells (cells_to_delete);
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::update_relations ()
|
|
{
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
c->sort_child_insts ();
|
|
}
|
|
|
|
std::vector <size_t> parent_insts (cells (), 0);
|
|
for (const_iterator c = begin (); c != end (); ++c) {
|
|
c->count_parent_insts (parent_insts);
|
|
}
|
|
std::vector <size_t>::const_iterator n = parent_insts.begin ();
|
|
for (iterator c = begin (); c != end (); ++c, ++n) {
|
|
c->clear_parent_insts (*n);
|
|
}
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
c->update_relations ();
|
|
}
|
|
}
|
|
|
|
Layout::top_down_iterator
|
|
Layout::end_top_cells ()
|
|
{
|
|
update ();
|
|
return m_top_down_list.begin () + m_top_cells;
|
|
}
|
|
|
|
Layout::top_down_const_iterator
|
|
Layout::end_top_cells () const
|
|
{
|
|
update ();
|
|
return m_top_down_list.begin () + m_top_cells;
|
|
}
|
|
|
|
void
|
|
Layout::force_update ()
|
|
{
|
|
if (hier_dirty () || bboxes_dirty ()) {
|
|
|
|
unsigned int invalid = m_invalid;
|
|
|
|
try {
|
|
|
|
m_invalid = std::numeric_limits<unsigned int>::max (); // prevent recursion
|
|
|
|
db::LayoutStateModel *state_model = const_cast<db::LayoutStateModel *> ((const db::LayoutStateModel *) this);
|
|
state_model->update ();
|
|
|
|
m_invalid = invalid;
|
|
|
|
} catch (...) {
|
|
m_invalid = invalid;
|
|
throw;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::update () const
|
|
{
|
|
if (! under_construction () && (hier_dirty () || bboxes_dirty ())) {
|
|
|
|
try {
|
|
|
|
m_invalid = std::numeric_limits<unsigned int>::max (); // prevent recursion
|
|
|
|
db::LayoutStateModel *state_model = const_cast<db::LayoutStateModel *> ((const db::LayoutStateModel *) this);
|
|
state_model->update ();
|
|
|
|
m_invalid = 0;
|
|
|
|
} catch (...) {
|
|
m_invalid = 0;
|
|
throw;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::do_update ()
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity, tl::to_string (tr ("Sorting")));
|
|
|
|
// establish a progress report since this operation can take some time.
|
|
// HINT: because of some gcc bug, automatic destruction of the tl::Progress
|
|
// object does not work. We overcome this problem by creating the object with new
|
|
// and catching exceptions.
|
|
// As this operation is critical we don't want to have it cancelled. Plus: do_update is called during ~LayoutLocker and
|
|
// if we throw exceptions then, we'll get a runtime assertion.
|
|
tl::RelativeProgress *pr = new tl::RelativeProgress (tl::to_string (tr ("Sorting layout")), m_cells_size, 0, false /*can't cancel*/);
|
|
pr->set_desc ("");
|
|
|
|
try {
|
|
|
|
// if the hierarchy has been changed so far, update
|
|
// the hierarchy management information
|
|
if (hier_dirty ()) {
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Updating relations");
|
|
pr->set_desc (tl::to_string (tr ("Updating relations")));
|
|
update_relations ();
|
|
}
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Topological sort");
|
|
pr->set_desc (tl::to_string (tr ("Topological sorting")));
|
|
tl_assert (topological_sort ());
|
|
}
|
|
}
|
|
|
|
// KLUDGE: a boolean vector (with size as determined by number of cells)
|
|
// would probably be much faster!
|
|
std::set<cell_index_type> dirty_parents;
|
|
|
|
// if something on the bboxes (either on shape level or on
|
|
// cell bbox level - i.e. by child instances) has been changed,
|
|
// update the bbox information. In addition sort the shapes
|
|
// lists of region queries, since they might have changed once
|
|
// the bboxes are dirty.
|
|
if (bboxes_dirty ()) {
|
|
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Updating bounding boxes");
|
|
unsigned int layers = 0;
|
|
pr->set (0);
|
|
pr->set_desc (tl::to_string (tr ("Updating bounding boxes")));
|
|
for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) {
|
|
++*pr;
|
|
cell_type &cp (cell (*c));
|
|
if (cp.is_shape_bbox_dirty () || dirty_parents.find (*c) != dirty_parents.end ()) {
|
|
if (cp.update_bbox (layers)) {
|
|
// the bounding box has changed - need to insert parents into "dirty parents" list
|
|
for (cell_type::parent_cell_iterator p = cp.begin_parent_cells (); p != cp.end_parent_cells (); ++p) {
|
|
dirty_parents.insert (*p);
|
|
}
|
|
}
|
|
}
|
|
if (cp.layers () > layers) {
|
|
layers = cp.layers ();
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Sorting shapes");
|
|
pr->set (0);
|
|
pr->set_desc (tl::to_string (tr ("Sorting shapes")));
|
|
for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) {
|
|
++*pr;
|
|
cell_type &cp (cell (*c));
|
|
cp.sort_shapes ();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// sort the instance trees now, since we have computed the bboxes
|
|
if (hier_dirty () || ! dirty_parents.empty ()) {
|
|
tl::SelfTimer timer (tl::verbosity () > layout_base_verbosity + 10, "Sorting instances");
|
|
size_t layers = 0;
|
|
pr->set (0);
|
|
pr->set_desc (tl::to_string (tr ("Sorting instances")));
|
|
for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) {
|
|
++*pr;
|
|
cell_type &cp (cell (*c));
|
|
bool force_sort_inst_tree = dirty_parents.find (*c) != dirty_parents.end ();
|
|
if (hier_dirty () || force_sort_inst_tree) {
|
|
cp.sort_inst_tree (force_sort_inst_tree);
|
|
}
|
|
if (cp.layers () > layers) {
|
|
layers = cp.layers ();
|
|
}
|
|
}
|
|
}
|
|
|
|
} catch (...) {
|
|
delete pr;
|
|
throw;
|
|
}
|
|
|
|
delete pr;
|
|
}
|
|
|
|
static Layout::meta_info_map s_empty_meta;
|
|
|
|
Layout::meta_info_iterator
|
|
Layout::begin_meta (db::cell_index_type ci) const
|
|
{
|
|
auto m = m_meta_info_by_cell.find (ci);
|
|
if (m != m_meta_info_by_cell.end ()) {
|
|
return m->second.begin ();
|
|
} else {
|
|
return s_empty_meta.begin ();
|
|
}
|
|
}
|
|
|
|
Layout::meta_info_iterator
|
|
Layout::end_meta (db::cell_index_type ci) const
|
|
{
|
|
auto m = m_meta_info_by_cell.find (ci);
|
|
if (m != m_meta_info_by_cell.end ()) {
|
|
return m->second.end ();
|
|
} else {
|
|
return s_empty_meta.end ();
|
|
}
|
|
}
|
|
|
|
const std::string &
|
|
Layout::meta_info_name (Layout::meta_info_name_id_type name_id) const
|
|
{
|
|
static std::string empty;
|
|
return name_id < m_meta_info_names.size () ? m_meta_info_names[name_id] : empty;
|
|
}
|
|
|
|
Layout::meta_info_name_id_type
|
|
Layout::meta_info_name_id (const std::string &name)
|
|
{
|
|
auto n = m_meta_info_name_map.find (name);
|
|
if (n != m_meta_info_name_map.end ()) {
|
|
return n->second;
|
|
} else {
|
|
size_t id = m_meta_info_names.size ();
|
|
m_meta_info_names.push_back (name);
|
|
m_meta_info_name_map.insert (std::make_pair (name, id));
|
|
return id;
|
|
}
|
|
}
|
|
|
|
Layout::meta_info_name_id_type
|
|
Layout::meta_info_name_id (const std::string &name) const
|
|
{
|
|
auto n = m_meta_info_name_map.find (name);
|
|
return n != m_meta_info_name_map.end () ? n->second : std::numeric_limits<meta_info_name_id_type>::max ();
|
|
}
|
|
|
|
void
|
|
Layout::clear_meta ()
|
|
{
|
|
if (manager () && manager ()->transacting ()) {
|
|
for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) {
|
|
manager ()->queue (this, new SetLayoutMetaInfoOp (i->first, &i->second, 0));
|
|
}
|
|
}
|
|
|
|
m_meta_info.clear ();
|
|
}
|
|
|
|
void
|
|
Layout::add_meta_info (meta_info_name_id_type name_id, const MetaInfo &i)
|
|
{
|
|
if (manager () && manager ()->transacting ()) {
|
|
auto e = m_meta_info.find (name_id);
|
|
manager ()->queue (this, new SetLayoutMetaInfoOp (name_id, e != m_meta_info.end () ? &e->second : 0, &i));
|
|
}
|
|
|
|
m_meta_info[name_id] = i;
|
|
}
|
|
|
|
void
|
|
Layout::remove_meta_info (meta_info_name_id_type name_id)
|
|
{
|
|
if (manager () && manager ()->transacting ()) {
|
|
auto e = m_meta_info.find (name_id);
|
|
if (e != m_meta_info.end ()) {
|
|
manager ()->queue (this, new SetLayoutMetaInfoOp (name_id, &e->second, 0));
|
|
}
|
|
}
|
|
|
|
m_meta_info.erase (name_id);
|
|
}
|
|
|
|
const MetaInfo &
|
|
Layout::meta_info (meta_info_name_id_type name_id) const
|
|
{
|
|
auto n = m_meta_info.find (name_id);
|
|
static MetaInfo null_value;
|
|
return n != m_meta_info.end () ? n->second : null_value;
|
|
}
|
|
|
|
bool
|
|
Layout::has_meta_info (meta_info_name_id_type name_id) const
|
|
{
|
|
return m_meta_info.find (name_id) != m_meta_info.end ();
|
|
}
|
|
|
|
void
|
|
Layout::clear_meta (db::cell_index_type ci)
|
|
{
|
|
if (manager () && manager ()->transacting ()) {
|
|
auto ib = begin_meta (ci);
|
|
auto ie = end_meta (ci);
|
|
for (auto i = ib; i != ie; ++i) {
|
|
manager ()->queue (this, new SetCellMetaInfoOp (ci, i->first, &i->second, 0));
|
|
}
|
|
}
|
|
|
|
m_meta_info_by_cell.erase (ci);
|
|
}
|
|
|
|
void
|
|
Layout::clear_all_meta ()
|
|
{
|
|
clear_meta ();
|
|
while (! m_meta_info_by_cell.empty ()) {
|
|
clear_meta (m_meta_info_by_cell.begin ()->first);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i)
|
|
{
|
|
if (manager () && manager ()->transacting ()) {
|
|
const MetaInfo *from = 0;
|
|
auto c = m_meta_info_by_cell.find (ci);
|
|
if (c != m_meta_info_by_cell.end ()) {
|
|
auto e = c->second.find (name_id);
|
|
if (e != c->second.end ()) {
|
|
from = &e->second;
|
|
}
|
|
}
|
|
manager ()->queue (this, new SetCellMetaInfoOp (ci, name_id, from, &i));
|
|
}
|
|
|
|
m_meta_info_by_cell[ci][name_id] = i;
|
|
}
|
|
|
|
void
|
|
Layout::remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id)
|
|
{
|
|
auto c = m_meta_info_by_cell.find (ci);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
const MetaInfo *from = 0;
|
|
if (c != m_meta_info_by_cell.end ()) {
|
|
auto e = c->second.find (name_id);
|
|
if (e != c->second.end ()) {
|
|
from = &e->second;
|
|
}
|
|
}
|
|
manager ()->queue (this, new SetCellMetaInfoOp (ci, name_id, from, 0));
|
|
}
|
|
|
|
if (c != m_meta_info_by_cell.end ()) {
|
|
c->second.erase (name_id);
|
|
}
|
|
}
|
|
|
|
const MetaInfo &
|
|
Layout::meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const
|
|
{
|
|
auto c = m_meta_info_by_cell.find (ci);
|
|
if (c != m_meta_info_by_cell.end ()) {
|
|
auto i = c->second.find (name_id);
|
|
if (i != c->second.end ()) {
|
|
return i->second;
|
|
}
|
|
}
|
|
|
|
static MetaInfo null_value;
|
|
return null_value;
|
|
}
|
|
|
|
bool
|
|
Layout::has_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const
|
|
{
|
|
auto c = m_meta_info_by_cell.find (ci);
|
|
if (c != m_meta_info_by_cell.end ()) {
|
|
return c->second.find (name_id) != c->second.end ();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::merge_meta_info (const db::Layout &other)
|
|
{
|
|
for (auto mi = other.begin_meta (); mi != other.end_meta (); ++mi) {
|
|
add_meta_info (other.meta_info_name (mi->first), mi->second);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::merge_meta_info (db::cell_index_type into_cell, const db::Layout &other, db::cell_index_type other_cell)
|
|
{
|
|
auto mi_begin = other.begin_meta (other_cell);
|
|
auto mi_end = other.end_meta (other_cell);
|
|
for (auto mi = mi_begin; mi != mi_end; ++mi) {
|
|
add_meta_info (into_cell, other.meta_info_name (mi->first), mi->second);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::merge_meta_info (const db::Layout &other, const db::CellMapping &cm)
|
|
{
|
|
for (auto i = cm.begin (); i != cm.end (); ++i) {
|
|
merge_meta_info (i->second, other, i->first);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::copy_meta_info (const db::Layout &other, const db::CellMapping &cm)
|
|
{
|
|
for (auto i = cm.begin (); i != cm.end (); ++i) {
|
|
copy_meta_info (i->second, other, i->first);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::swap_layers (unsigned int a, unsigned int b)
|
|
{
|
|
tl_assert (m_layers.layer_state (a) != LayoutLayers::Free);
|
|
tl_assert (m_layers.layer_state (b) != LayoutLayers::Free);
|
|
|
|
// clear the shapes
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
c->swap (a, b);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::move_layer (unsigned int src, unsigned int dest)
|
|
{
|
|
tl_assert (m_layers.layer_state (src) != LayoutLayers::Free);
|
|
tl_assert (m_layers.layer_state (dest) != LayoutLayers::Free);
|
|
|
|
// move the shapes
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
c->move (src, dest);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::move_layer (unsigned int src, unsigned int dest, unsigned int flags)
|
|
{
|
|
tl_assert (m_layers.layer_state (src) != LayoutLayers::Free);
|
|
tl_assert (m_layers.layer_state (dest) != LayoutLayers::Free);
|
|
|
|
// move the shapes
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
c->move (src, dest, flags);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::copy_layer (unsigned int src, unsigned int dest)
|
|
{
|
|
tl_assert (m_layers.layer_state (src) != LayoutLayers::Free);
|
|
tl_assert (m_layers.layer_state (dest) != LayoutLayers::Free);
|
|
|
|
// copy the shapes
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
c->copy (src, dest);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::copy_layer (unsigned int src, unsigned int dest, unsigned int flags)
|
|
{
|
|
tl_assert (m_layers.layer_state (src) != LayoutLayers::Free);
|
|
tl_assert (m_layers.layer_state (dest) != LayoutLayers::Free);
|
|
|
|
// copy the shapes
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
c->copy (src, dest, flags);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::clear_layer (unsigned int n)
|
|
{
|
|
tl_assert (m_layers.layer_state (n) != LayoutLayers::Free);
|
|
|
|
// clear the shapes
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
c->clear (n);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::clear_layer (unsigned int n, unsigned int flags)
|
|
{
|
|
tl_assert (m_layers.layer_state (n) != LayoutLayers::Free);
|
|
|
|
// clear the shapes
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
c->clear (n, flags);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::delete_layer (unsigned int n)
|
|
{
|
|
tl_assert (m_layers.layer_state (n) != LayoutLayers::Free);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new InsertRemoveLayerOp (n, m_layers.get_properties (n), false /*delete*/));
|
|
}
|
|
|
|
m_layers.delete_layer (n);
|
|
|
|
// clear the shapes
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
c->clear (n);
|
|
}
|
|
|
|
layer_properties_changed ();
|
|
}
|
|
|
|
unsigned int
|
|
Layout::get_layer (const db::LayerProperties &props)
|
|
{
|
|
int li = get_layer_maybe (props);
|
|
if (li >= 0) {
|
|
return (unsigned int) li;
|
|
}
|
|
|
|
if (props.is_null ()) {
|
|
// for a null layer info always create a layer
|
|
return insert_layer ();
|
|
} else {
|
|
return insert_layer (props);
|
|
}
|
|
}
|
|
|
|
unsigned int
|
|
Layout::insert_layer (const LayerProperties &props)
|
|
{
|
|
unsigned int i = m_layers.insert_layer (props);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new InsertRemoveLayerOp (i, props, true/*insert*/));
|
|
}
|
|
|
|
layer_properties_changed ();
|
|
|
|
return i;
|
|
}
|
|
|
|
void
|
|
Layout::insert_layer (unsigned int index, const LayerProperties &props)
|
|
{
|
|
m_layers.insert_layer (index, props);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new InsertRemoveLayerOp (index, props, true/*insert*/));
|
|
}
|
|
|
|
layer_properties_changed ();
|
|
}
|
|
|
|
unsigned int
|
|
Layout::insert_special_layer (const LayerProperties &props)
|
|
{
|
|
unsigned int i = m_layers.insert_special_layer (props);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new InsertRemoveLayerOp (i, props, true/*insert*/));
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
void
|
|
Layout::set_properties (unsigned int i, const LayerProperties &props)
|
|
{
|
|
if (m_layers.get_properties (i) != props) {
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new SetLayerPropertiesOp (i, props, m_layers.get_properties (i)));
|
|
}
|
|
|
|
m_layers.set_properties (i, props);
|
|
|
|
layer_properties_changed ();
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::insert_special_layer (unsigned int index, const LayerProperties &props)
|
|
{
|
|
m_layers.insert_special_layer (index, props);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new InsertRemoveLayerOp (index, props, true/*insert*/));
|
|
}
|
|
}
|
|
|
|
static const std::vector<tl::Variant> &gauge_parameters (const std::vector<tl::Variant> &p, const db::PCellDeclaration *pcell_decl, std::vector<tl::Variant> &buffer)
|
|
{
|
|
const std::vector<db::PCellParameterDeclaration> &pcp = pcell_decl->parameter_declarations ();
|
|
|
|
if (pcp.size () > p.size ()) {
|
|
|
|
buffer.clear ();
|
|
buffer.resize (pcp.size ());
|
|
buffer = p;
|
|
for (std::vector<PCellParameterDeclaration>::const_iterator i = pcp.begin () + p.size (); i != pcp.end (); ++i) {
|
|
buffer.push_back (i->get_default ());
|
|
}
|
|
return buffer;
|
|
|
|
} else if (pcp.size () < p.size ()) {
|
|
|
|
buffer.clear ();
|
|
buffer.insert (buffer.end (), p.begin (), p.begin () + pcp.size ());
|
|
return buffer;
|
|
|
|
} else {
|
|
return p;
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::replace_cell (cell_index_type target_cell_index, db::Cell *new_cell, bool retain_layout)
|
|
{
|
|
invalidate_hier ();
|
|
|
|
db::Cell *old_cell = m_cell_ptrs [target_cell_index];
|
|
if (old_cell) {
|
|
old_cell->unregister ();
|
|
if (retain_layout) {
|
|
new_cell->Cell::operator= (*old_cell);
|
|
}
|
|
}
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
// note the "take" method - this takes out the cell but does not delete it (we need it inside undo)
|
|
m_cells.take (iterator (old_cell));
|
|
manager ()->queue (this, new NewRemoveCellOp (target_cell_index, cell_name (target_cell_index), true /*remove*/, old_cell));
|
|
} else {
|
|
m_cells.erase (iterator (old_cell));
|
|
}
|
|
|
|
m_cells.push_back_ptr (new_cell);
|
|
m_cell_ptrs [target_cell_index] = new_cell;
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new NewRemoveCellOp (target_cell_index, m_cell_names [target_cell_index], false /*new*/, 0));
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::replace_instances_of (cell_index_type src_cell_index, cell_index_type target_cell_index)
|
|
{
|
|
// replace all instances of the new cell with the original one
|
|
std::vector<std::pair<db::cell_index_type, db::Instance> > parents;
|
|
for (db::Cell::parent_inst_iterator pi = cell (src_cell_index).begin_parent_insts (); ! pi.at_end (); ++pi) {
|
|
parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ()));
|
|
}
|
|
|
|
for (std::vector<std::pair<db::cell_index_type, db::Instance> >::const_iterator p = parents.begin (); p != parents.end (); ++p) {
|
|
db::CellInstArray ia = p->second.cell_inst ();
|
|
ia.object ().cell_index (target_cell_index);
|
|
cell (p->first).replace (p->second, ia);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::get_pcell_variant_as (pcell_id_type pcell_id, const std::vector<tl::Variant> &p, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping, bool retain_layout)
|
|
{
|
|
pcell_header_type *header = pcell_header (pcell_id);
|
|
tl_assert (header != 0);
|
|
|
|
std::vector<tl::Variant> buffer;
|
|
const std::vector<tl::Variant> ¶meters = gauge_parameters (p, header->declaration (), buffer);
|
|
|
|
// this variant must not exist yet for "get as" semantics
|
|
tl_assert (header->get_variant (*this, parameters) == 0);
|
|
|
|
tl_assert (m_cell_ptrs [target_cell_index] != 0);
|
|
|
|
pcell_variant_type *variant = new pcell_variant_type (target_cell_index, *this, pcell_id, parameters);
|
|
replace_cell (target_cell_index, variant, retain_layout);
|
|
|
|
if (! retain_layout) {
|
|
// produce the layout unless we retained it
|
|
variant->update (layer_mapping);
|
|
}
|
|
}
|
|
|
|
cell_index_type
|
|
Layout::get_pcell_variant_dict (pcell_id_type pcell_id, const std::map<std::string, tl::Variant> &p)
|
|
{
|
|
pcell_header_type *header = pcell_header (pcell_id);
|
|
tl_assert (header != 0);
|
|
|
|
std::vector<tl::Variant> parameters;
|
|
const std::vector<db::PCellParameterDeclaration> &pcp = header->declaration ()->parameter_declarations ();
|
|
parameters.reserve (pcp.size ());
|
|
for (std::vector<db::PCellParameterDeclaration>::const_iterator pd = pcp.begin (); pd != pcp.end(); ++pd) {
|
|
std::map<std::string, tl::Variant>::const_iterator pp = p.find (pd->get_name ());
|
|
if (pp == p.end ()) {
|
|
parameters.push_back (pd->get_default ());
|
|
} else {
|
|
parameters.push_back (pp->second);
|
|
}
|
|
}
|
|
|
|
pcell_variant_type *variant = header->get_variant (*this, parameters);
|
|
if (! variant) {
|
|
|
|
std::string b (header->get_name ());
|
|
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
|
|
b = uniquify_cell_name (b.c_str ());
|
|
}
|
|
|
|
// create a new cell
|
|
cell_index_type new_index = allocate_new_cell ();
|
|
|
|
variant = new pcell_variant_type (new_index, *this, pcell_id, parameters);
|
|
m_cells.push_back_ptr (variant);
|
|
m_cell_ptrs [new_index] = variant;
|
|
|
|
// enter its index and cell_name
|
|
register_cell_name (b.c_str (), new_index);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
|
|
}
|
|
|
|
// produce the layout
|
|
variant->update ();
|
|
|
|
}
|
|
|
|
return variant->cell_index ();
|
|
}
|
|
|
|
cell_index_type
|
|
Layout::get_pcell_variant (pcell_id_type pcell_id, const std::vector<tl::Variant> &p)
|
|
{
|
|
pcell_header_type *header = pcell_header (pcell_id);
|
|
tl_assert (header != 0);
|
|
|
|
std::vector<tl::Variant> buffer;
|
|
const std::vector<tl::Variant> ¶meters = gauge_parameters (p, header->declaration (), buffer);
|
|
|
|
pcell_variant_type *variant = header->get_variant (*this, parameters);
|
|
if (! variant) {
|
|
|
|
std::string b (header->get_name ());
|
|
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
|
|
b = uniquify_cell_name (b.c_str ());
|
|
}
|
|
|
|
// create a new cell
|
|
cell_index_type new_index = allocate_new_cell ();
|
|
|
|
variant = new pcell_variant_type (new_index, *this, pcell_id, parameters);
|
|
m_cells.push_back_ptr (variant);
|
|
m_cell_ptrs [new_index] = variant;
|
|
|
|
// enter its index and cell_name
|
|
register_cell_name (b.c_str (), new_index);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
|
|
}
|
|
|
|
// produce the layout
|
|
variant->update ();
|
|
|
|
}
|
|
|
|
return variant->cell_index ();
|
|
}
|
|
|
|
const Layout::pcell_header_type *
|
|
Layout::pcell_header (pcell_id_type pcell_id) const
|
|
{
|
|
return (const_cast<db::Layout *> (this))->pcell_header (pcell_id);
|
|
}
|
|
|
|
Layout::pcell_header_type *
|
|
Layout::pcell_header (pcell_id_type pcell_id)
|
|
{
|
|
if (pcell_id >= m_pcells.size ()) {
|
|
return 0;
|
|
} else {
|
|
return m_pcells [pcell_id];
|
|
}
|
|
}
|
|
|
|
std::pair<bool, pcell_id_type>
|
|
Layout::pcell_by_name (const char *name) const
|
|
{
|
|
std::map<std::string, pcell_id_type>::const_iterator pcid = m_pcell_ids.find (std::string (name));
|
|
if (pcid != m_pcell_ids.end ()) {
|
|
return std::make_pair (true, pcid->second);
|
|
} else {
|
|
return std::make_pair (false, pcell_id_type (0));
|
|
}
|
|
}
|
|
|
|
pcell_id_type
|
|
Layout::register_pcell (const std::string &name, pcell_declaration_type *declaration)
|
|
{
|
|
// No undo/redo support for PCell registration. The interactions with PCell variants
|
|
// (for which undo/redo support is available) is too complex ...
|
|
tl_assert (!manager () || !manager ()->transacting ());
|
|
|
|
pcell_id_type id;
|
|
|
|
pcell_name_map::const_iterator pcid = m_pcell_ids.find (name);
|
|
if (pcid != m_pcell_ids.end ()) {
|
|
|
|
// replace any existing PCell declaration with that name.
|
|
id = pcid->second;
|
|
if (m_pcells [id]) {
|
|
delete m_pcells [id];
|
|
}
|
|
|
|
m_pcells [id] = new pcell_header_type (id, name, declaration);
|
|
|
|
} else {
|
|
|
|
id = (unsigned int) m_pcells.size ();
|
|
m_pcells.push_back (new pcell_header_type (id, name, declaration));
|
|
m_pcell_ids.insert (std::make_pair (std::string (name), id));
|
|
|
|
}
|
|
|
|
declaration->m_id = id;
|
|
declaration->m_name = name;
|
|
declaration->mp_layout = this;
|
|
|
|
// marks this object being held by the layout
|
|
declaration->keep ();
|
|
|
|
return id;
|
|
}
|
|
|
|
const Layout::pcell_declaration_type *
|
|
Layout::pcell_declaration (pcell_id_type pcell_id) const
|
|
{
|
|
const pcell_header_type *header = pcell_header (pcell_id);
|
|
return header ? header->declaration () : 0;
|
|
}
|
|
|
|
db::cell_index_type
|
|
Layout::convert_cell_to_static (db::cell_index_type ci)
|
|
{
|
|
tl_assert (is_valid_cell_index (ci));
|
|
db::cell_index_type ret_ci = ci;
|
|
|
|
if (m_cell_ptrs [ci] && m_cell_ptrs [ci]->is_proxy ()) {
|
|
|
|
invalidate_hier ();
|
|
|
|
const cell_type &org_cell = cell (ci);
|
|
|
|
// Note: convert to static cell by explicitly cloning to the db::Cell class
|
|
ret_ci = add_cell (org_cell.get_basic_name ().c_str ());
|
|
cell_type &new_cell = cell (ret_ci);
|
|
new_cell = org_cell;
|
|
new_cell.set_cell_index (ret_ci);
|
|
|
|
// remove guiding shapes.
|
|
if (m_layers.guiding_shape_layer_maybe () >= 0) {
|
|
new_cell.shapes (m_layers.guiding_shape_layer_maybe ()).clear ();
|
|
}
|
|
|
|
}
|
|
|
|
return ret_ci;
|
|
}
|
|
|
|
std::pair<bool, db::pcell_id_type>
|
|
Layout::is_pcell_instance (cell_index_type cell_index) const
|
|
{
|
|
const Cell *child_cell = &cell (cell_index);
|
|
|
|
const LibraryProxy *lib_proxy = dynamic_cast<const LibraryProxy *> (child_cell);
|
|
if (lib_proxy) {
|
|
Library *lib = LibraryManager::instance ().lib (lib_proxy->lib_id ());
|
|
tl_assert (lib != 0);
|
|
return lib->layout ().is_pcell_instance (lib_proxy->library_cell_index ());
|
|
}
|
|
|
|
const PCellVariant *pcell_variant = dynamic_cast<const PCellVariant *> (child_cell);
|
|
if (pcell_variant) {
|
|
return std::make_pair (true, pcell_variant->pcell_id ());
|
|
} else {
|
|
return std::make_pair (false, db::pcell_id_type(0));
|
|
}
|
|
}
|
|
|
|
const Layout::pcell_declaration_type *
|
|
Layout::pcell_declaration_for_pcell_variant (cell_index_type variant_cell_index) const
|
|
{
|
|
const Cell *variant_cell = &cell (variant_cell_index);
|
|
|
|
const LibraryProxy *lib_proxy = dynamic_cast<const LibraryProxy *> (variant_cell);
|
|
if (lib_proxy) {
|
|
Library *lib = LibraryManager::instance ().lib (lib_proxy->lib_id ());
|
|
tl_assert (lib != 0);
|
|
return lib->layout ().pcell_declaration_for_pcell_variant (lib_proxy->library_cell_index ());
|
|
}
|
|
|
|
const PCellVariant *pcell_variant = dynamic_cast<const PCellVariant *> (variant_cell);
|
|
if (pcell_variant) {
|
|
return pcell_declaration (pcell_variant->pcell_id ());
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
std::pair<db::Library *, db::cell_index_type>
|
|
Layout::defining_library (cell_index_type cell_index) const
|
|
{
|
|
const db::Layout *layout = this;
|
|
db::Library *lib = 0;
|
|
|
|
while (true) {
|
|
|
|
const Cell *child_cell = &layout->cell (cell_index);
|
|
const LibraryProxy *lib_proxy = dynamic_cast<const LibraryProxy *> (child_cell);
|
|
if (lib_proxy) {
|
|
|
|
lib = LibraryManager::instance ().lib (lib_proxy->lib_id ());
|
|
tl_assert (lib != 0);
|
|
cell_index = lib_proxy->library_cell_index ();
|
|
layout = &lib->layout ();
|
|
|
|
} else {
|
|
return std::pair<db::Library *, db::cell_index_type> (lib, cell_index);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
tl::Variant
|
|
Layout::get_pcell_parameter (cell_index_type cell_index, const std::string &name) const
|
|
{
|
|
const Cell *child_cell = &cell (cell_index);
|
|
|
|
const LibraryProxy *lib_proxy = dynamic_cast<const LibraryProxy *> (child_cell);
|
|
if (lib_proxy) {
|
|
Library *lib = LibraryManager::instance ().lib (lib_proxy->lib_id ());
|
|
tl_assert (lib != 0);
|
|
return lib->layout ().get_pcell_parameter (lib_proxy->library_cell_index (), name);
|
|
}
|
|
|
|
const PCellVariant *pcell_variant = dynamic_cast<const PCellVariant *> (child_cell);
|
|
if (pcell_variant) {
|
|
return pcell_variant->parameter_by_name (name);
|
|
} else {
|
|
return tl::Variant ();
|
|
}
|
|
}
|
|
|
|
std::map<std::string, tl::Variant>
|
|
Layout::get_named_pcell_parameters (cell_index_type cell_index) const
|
|
{
|
|
const Cell *child_cell = &cell (cell_index);
|
|
|
|
const LibraryProxy *lib_proxy = dynamic_cast<const LibraryProxy *> (child_cell);
|
|
if (lib_proxy) {
|
|
Library *lib = LibraryManager::instance ().lib (lib_proxy->lib_id ());
|
|
tl_assert (lib != 0);
|
|
return lib->layout ().get_named_pcell_parameters (lib_proxy->library_cell_index ());
|
|
}
|
|
|
|
const PCellVariant *pcell_variant = dynamic_cast<const PCellVariant *> (child_cell);
|
|
if (pcell_variant) {
|
|
return pcell_variant->parameters_by_name ();
|
|
} else {
|
|
return std::map<std::string, tl::Variant> ();
|
|
}
|
|
}
|
|
|
|
const std::vector<tl::Variant> &
|
|
Layout::get_pcell_parameters (cell_index_type cell_index) const
|
|
{
|
|
const Cell *child_cell = &cell (cell_index);
|
|
|
|
const LibraryProxy *lib_proxy = dynamic_cast<const LibraryProxy *> (child_cell);
|
|
if (lib_proxy) {
|
|
Library *lib = LibraryManager::instance ().lib (lib_proxy->lib_id ());
|
|
tl_assert (lib != 0);
|
|
return lib->layout ().get_pcell_parameters (lib_proxy->library_cell_index ());
|
|
}
|
|
|
|
const PCellVariant *pcell_variant = dynamic_cast<const PCellVariant *> (child_cell);
|
|
if (pcell_variant) {
|
|
return pcell_variant->parameters ();
|
|
} else {
|
|
static std::vector<tl::Variant> empty;
|
|
return empty;
|
|
}
|
|
}
|
|
|
|
cell_index_type
|
|
Layout::get_pcell_variant_cell (cell_index_type cell_index, const std::vector<tl::Variant> &new_parameters)
|
|
{
|
|
Cell *child_cell = &cell (cell_index);
|
|
|
|
const LibraryProxy *lib_proxy = dynamic_cast<const LibraryProxy *> (child_cell);
|
|
if (lib_proxy) {
|
|
|
|
Library *lib = LibraryManager::instance ().lib (lib_proxy->lib_id ());
|
|
cell_index_type new_lib_cell_index = lib->layout ().get_pcell_variant_cell (lib_proxy->library_cell_index (), new_parameters);
|
|
|
|
if (new_lib_cell_index != lib_proxy->library_cell_index ()) {
|
|
return get_lib_proxy (lib, new_lib_cell_index);
|
|
}
|
|
|
|
} else {
|
|
|
|
PCellVariant *pcell_variant = dynamic_cast<PCellVariant *> (child_cell);
|
|
if (pcell_variant) {
|
|
return get_pcell_variant (pcell_variant->pcell_id (), new_parameters);
|
|
}
|
|
|
|
}
|
|
|
|
return cell_index;
|
|
|
|
}
|
|
|
|
bool
|
|
Layout::has_context_info () const
|
|
{
|
|
for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) {
|
|
if (i->second.persisted) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
Layout::has_context_info (cell_index_type cell_index) const
|
|
{
|
|
auto c = m_meta_info_by_cell.find (cell_index);
|
|
if (c != m_meta_info_by_cell.end ()) {
|
|
for (auto i = c->second.begin (); i != c->second.end (); ++i) {
|
|
if (i->second.persisted) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
const db::Cell &cref = cell (cell_index);
|
|
if (cref.is_proxy () && ! cref.is_top ()) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool
|
|
Layout::get_context_info (std::vector <std::string> &strings) const
|
|
{
|
|
LayoutOrCellContextInfo info;
|
|
if (! get_context_info (info)) {
|
|
return false;
|
|
} else {
|
|
info.serialize (strings);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
Layout::get_context_info (LayoutOrCellContextInfo &info) const
|
|
{
|
|
for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) {
|
|
if (i->second.persisted) {
|
|
std::pair<tl::Variant, std::string> &mi = info.meta_info [m_meta_info_names [i->first] ];
|
|
mi.first = i->second.value;
|
|
mi.second = i->second.description;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
Layout::fill_meta_info_from_context (std::vector <std::string>::const_iterator from, std::vector <std::string>::const_iterator to)
|
|
{
|
|
fill_meta_info_from_context (LayoutOrCellContextInfo::deserialize (from, to));
|
|
}
|
|
|
|
void
|
|
Layout::fill_meta_info_from_context (const LayoutOrCellContextInfo &context_info)
|
|
{
|
|
if (! context_info.meta_info.empty ()) {
|
|
for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) {
|
|
meta_info_name_id_type name_id = meta_info_name_id (i->first);
|
|
m_meta_info [name_id] = MetaInfo (i->second.second, i->second.first, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
Layout::get_context_info (cell_index_type cell_index, std::vector <std::string> &strings) const
|
|
{
|
|
LayoutOrCellContextInfo info;
|
|
if (! get_context_info (cell_index, info)) {
|
|
return false;
|
|
} else {
|
|
info.serialize (strings);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &info) const
|
|
{
|
|
bool any_meta = false;
|
|
|
|
auto cmi = m_meta_info_by_cell.find (cell_index);
|
|
if (cmi != m_meta_info_by_cell.end ()) {
|
|
for (auto i = cmi->second.begin (); i != cmi->second.end (); ++i) {
|
|
if (i->second.persisted) {
|
|
std::pair<tl::Variant, std::string> &mi = info.meta_info [m_meta_info_names [i->first] ];
|
|
mi.first = i->second.value;
|
|
mi.second = i->second.description;
|
|
any_meta = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
const db::Cell *cptr = &cell (cell_index);
|
|
|
|
const db::ColdProxy *cold_proxy = dynamic_cast <const db::ColdProxy *> (cptr);
|
|
if (cold_proxy) {
|
|
info = cold_proxy->context_info ();
|
|
return true;
|
|
}
|
|
|
|
const db::Layout *ly = this;
|
|
|
|
const db::LibraryProxy *lib_proxy;
|
|
while (ly != 0 && (lib_proxy = dynamic_cast <const db::LibraryProxy *> (cptr)) != 0) {
|
|
|
|
const db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ());
|
|
if (! lib) {
|
|
return any_meta; // abort
|
|
} else {
|
|
|
|
// one level of library indirection
|
|
ly = &lib->layout ();
|
|
cptr = &ly->cell (lib_proxy->library_cell_index ());
|
|
info.lib_name = lib->get_name ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const db::PCellVariant *pcell_variant = dynamic_cast <const db::PCellVariant *> (cptr);
|
|
if (pcell_variant) {
|
|
|
|
const db::PCellDeclaration *pcell_decl = ly->pcell_declaration (pcell_variant->pcell_id ());
|
|
|
|
const std::vector<db::PCellParameterDeclaration> &pcp = pcell_decl->parameter_declarations ();
|
|
std::vector<db::PCellParameterDeclaration>::const_iterator pd = pcp.begin ();
|
|
for (std::vector<tl::Variant>::const_iterator p = pcell_variant->parameters ().begin (); p != pcell_variant->parameters ().end () && pd != pcp.end (); ++p, ++pd) {
|
|
info.pcell_parameters.insert (std::make_pair (pd->get_name (), *p));
|
|
}
|
|
|
|
const db::PCellHeader *header = ly->pcell_header (pcell_variant->pcell_id ());
|
|
info.pcell_name = header->get_name ();
|
|
|
|
} else if (ly != this) {
|
|
info.cell_name = ly->cell_name (cptr->cell_index ());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
Layout::fill_meta_info_from_context (cell_index_type cell_index, std::vector <std::string>::const_iterator from, std::vector <std::string>::const_iterator to)
|
|
{
|
|
fill_meta_info_from_context (cell_index, LayoutOrCellContextInfo::deserialize (from, to));
|
|
}
|
|
|
|
void
|
|
Layout::fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info)
|
|
{
|
|
if (! context_info.meta_info.empty ()) {
|
|
|
|
meta_info_map &mi = m_meta_info_by_cell [cell_index];
|
|
|
|
for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) {
|
|
meta_info_name_id_type name_id = meta_info_name_id (i->first);
|
|
mi [name_id] = MetaInfo (i->second.second, i->second.first, true);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::restore_proxies (ImportLayerMapping *layer_mapping)
|
|
{
|
|
std::vector<db::ColdProxy *> cold_proxies;
|
|
|
|
for (iterator c = begin (); c != end (); ++c) {
|
|
db::ColdProxy *proxy = dynamic_cast<db::ColdProxy *> (c.operator-> ());
|
|
if (proxy) {
|
|
cold_proxies.push_back (proxy);
|
|
}
|
|
}
|
|
|
|
bool needs_cleanup = false;
|
|
for (std::vector<db::ColdProxy *>::const_iterator p = cold_proxies.begin (); p != cold_proxies.end (); ++p) {
|
|
if (recover_proxy_as ((*p)->cell_index (), (*p)->context_info (), layer_mapping)) {
|
|
needs_cleanup = true;
|
|
}
|
|
}
|
|
|
|
if (needs_cleanup) {
|
|
cleanup ();
|
|
}
|
|
}
|
|
|
|
bool
|
|
Layout::recover_proxy_as (cell_index_type cell_index, std::vector <std::string>::const_iterator from, std::vector <std::string>::const_iterator to, ImportLayerMapping *layer_mapping)
|
|
{
|
|
if (from == to) {
|
|
return false;
|
|
}
|
|
|
|
return recover_proxy_as (cell_index, LayoutOrCellContextInfo::deserialize (from, to), layer_mapping);
|
|
}
|
|
|
|
bool
|
|
Layout::recover_proxy_as (cell_index_type cell_index, const LayoutOrCellContextInfo &info, ImportLayerMapping *layer_mapping)
|
|
{
|
|
if (! info.lib_name.empty ()) {
|
|
|
|
db::Cell *lib_cell = 0;
|
|
|
|
Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (info.lib_name, m_tech_name);
|
|
if (lib) {
|
|
lib_cell = lib->layout ().recover_proxy_no_lib (info);
|
|
}
|
|
|
|
if (lib_cell) {
|
|
get_lib_proxy_as (lib, lib_cell->cell_index (), cell_index, layer_mapping);
|
|
return true;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (! info.pcell_name.empty ()) {
|
|
|
|
std::pair<bool, pcell_id_type> pc = pcell_by_name (info.pcell_name.c_str ());
|
|
if (pc.first) {
|
|
get_pcell_variant_as (pc.second, pcell_declaration (pc.second)->map_parameters (info.pcell_parameters), cell_index, layer_mapping);
|
|
return true;
|
|
}
|
|
|
|
} else if (! info.cell_name.empty ()) {
|
|
|
|
// This should not happen. A cell (given by the cell name) cannot be proxy to another cell in the same layout.
|
|
tl_assert (false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! dynamic_cast<db::ColdProxy *> (m_cell_ptrs [cell_index])) {
|
|
// create a cold proxy representing the context information so we can restore it
|
|
create_cold_proxy_as (info, cell_index);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
db::Cell *
|
|
Layout::recover_proxy (std::vector <std::string>::const_iterator from, std::vector <std::string>::const_iterator to)
|
|
{
|
|
if (from == to) {
|
|
return 0;
|
|
}
|
|
|
|
return recover_proxy (LayoutOrCellContextInfo::deserialize (from, to));
|
|
}
|
|
|
|
db::Cell *
|
|
Layout::recover_proxy (const LayoutOrCellContextInfo &info)
|
|
{
|
|
if (! info.lib_name.empty ()) {
|
|
|
|
Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (info.lib_name, m_tech_name);
|
|
|
|
db::Cell *lib_cell = 0;
|
|
if (lib) {
|
|
lib_cell = lib->layout ().recover_proxy_no_lib (info);
|
|
}
|
|
|
|
if (lib_cell) {
|
|
return m_cell_ptrs [get_lib_proxy (lib, lib_cell->cell_index ())];
|
|
}
|
|
|
|
} else {
|
|
|
|
db::Cell *proxy = recover_proxy_no_lib (info);
|
|
if (proxy) {
|
|
return proxy;
|
|
}
|
|
|
|
}
|
|
|
|
return m_cell_ptrs [create_cold_proxy (info)];
|
|
}
|
|
|
|
db::Cell *
|
|
Layout::recover_proxy_no_lib (const LayoutOrCellContextInfo &info)
|
|
{
|
|
if (! info.pcell_name.empty ()) {
|
|
|
|
std::pair<bool, pcell_id_type> pc = pcell_by_name (info.pcell_name.c_str ());
|
|
if (pc.first) {
|
|
cell_index_type cell_index = get_pcell_variant (pc.second, pcell_declaration (pc.second)->map_parameters (info.pcell_parameters));
|
|
return m_cell_ptrs [cell_index];
|
|
}
|
|
|
|
} else if (! info.cell_name.empty ()) {
|
|
|
|
std::pair<bool, cell_index_type> cc = cell_by_name (info.cell_name.c_str ());
|
|
if (cc.first) {
|
|
return m_cell_ptrs [cc.second];
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
std::string
|
|
Layout::display_name (cell_index_type cell_index) const
|
|
{
|
|
return cell (cell_index).get_display_name ();
|
|
}
|
|
|
|
std::string
|
|
Layout::basic_name (cell_index_type cell_index) const
|
|
{
|
|
return cell (cell_index).get_basic_name ();
|
|
}
|
|
|
|
void
|
|
Layout::register_lib_proxy (db::LibraryProxy *lib_proxy)
|
|
{
|
|
m_lib_proxy_map.insert (std::make_pair (std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ()), lib_proxy->Cell::cell_index ()));
|
|
}
|
|
|
|
void
|
|
Layout::unregister_lib_proxy (db::LibraryProxy *lib_proxy)
|
|
{
|
|
m_lib_proxy_map.erase (std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ()));
|
|
}
|
|
|
|
void
|
|
Layout::get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping, bool retain_layout)
|
|
{
|
|
tl_assert (m_cell_ptrs [target_cell_index] != 0);
|
|
|
|
LibraryProxy *proxy = new LibraryProxy (target_cell_index, *this, lib->get_id (), cell_index);
|
|
replace_cell (target_cell_index, proxy, retain_layout);
|
|
|
|
if (! retain_layout) {
|
|
// produce the layout unless we retained it
|
|
proxy->update (layer_mapping);
|
|
}
|
|
}
|
|
|
|
cell_index_type
|
|
Layout::get_lib_proxy (Library *lib, cell_index_type cell_index)
|
|
{
|
|
lib_proxy_map::const_iterator lp = m_lib_proxy_map.find (std::make_pair (lib->get_id (), cell_index));
|
|
if (lp != m_lib_proxy_map.end ()) {
|
|
return lp->second;
|
|
} else {
|
|
|
|
// create a new unique name
|
|
std::string b (lib->layout ().basic_name (cell_index));
|
|
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
|
|
b = uniquify_cell_name (b.c_str ());
|
|
}
|
|
|
|
// create a new cell (a LibraryProxy)
|
|
cell_index_type new_index = allocate_new_cell ();
|
|
|
|
LibraryProxy *proxy = new LibraryProxy (new_index, *this, lib->get_id (), cell_index);
|
|
m_cells.push_back_ptr (proxy);
|
|
m_cell_ptrs [new_index] = proxy;
|
|
|
|
// enter its index and cell_name
|
|
register_cell_name (b.c_str (), new_index);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
|
|
}
|
|
|
|
// produce the layout
|
|
proxy->update ();
|
|
|
|
return new_index;
|
|
|
|
}
|
|
}
|
|
|
|
cell_index_type
|
|
Layout::create_cold_proxy (const db::LayoutOrCellContextInfo &info)
|
|
{
|
|
// create a new unique name
|
|
std::string b;
|
|
if (! info.cell_name.empty ()) {
|
|
b = info.cell_name;
|
|
} else if (! info.pcell_name.empty ()) {
|
|
b = info.pcell_name;
|
|
}
|
|
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
|
|
b = uniquify_cell_name (b.c_str ());
|
|
}
|
|
|
|
// create a new cell (a LibraryProxy)
|
|
cell_index_type new_index = allocate_new_cell ();
|
|
|
|
ColdProxy *proxy = new ColdProxy (new_index, *this, info);
|
|
m_cells.push_back_ptr (proxy);
|
|
m_cell_ptrs [new_index] = proxy;
|
|
|
|
// enter its index and cell_name
|
|
register_cell_name (b.c_str (), new_index);
|
|
|
|
if (manager () && manager ()->transacting ()) {
|
|
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
|
|
}
|
|
|
|
return new_index;
|
|
}
|
|
|
|
void
|
|
Layout::create_cold_proxy_as (const db::LayoutOrCellContextInfo &info, cell_index_type target_cell_index)
|
|
{
|
|
tl_assert (m_cell_ptrs [target_cell_index] != 0);
|
|
|
|
ColdProxy *proxy = new ColdProxy (target_cell_index, *this, info);
|
|
replace_cell (target_cell_index, proxy, true);
|
|
}
|
|
|
|
void
|
|
Layout::redo (db::Op *op)
|
|
{
|
|
const LayoutOp *layout_op = dynamic_cast <const LayoutOp *> (op);
|
|
if (layout_op) {
|
|
layout_op->redo (this);
|
|
}
|
|
}
|
|
|
|
void
|
|
Layout::undo (db::Op *op)
|
|
{
|
|
const LayoutOp *layout_op = dynamic_cast <const LayoutOp *> (op);
|
|
if (layout_op) {
|
|
layout_op->undo (this);
|
|
}
|
|
}
|
|
|
|
}
|
|
|