Improving Layout::cleanup to consolidate library and cold proxies and to establish proper cell names if possible

This commit is contained in:
Matthias Koefferlein 2026-04-02 22:14:11 +02:00
parent dcc7f28c77
commit a6dce8c2ad
12 changed files with 323 additions and 50 deletions

View File

@ -60,14 +60,36 @@ ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOr
}
i->second->push_back (this);
}
layout.register_cold_proxy (this);
}
ColdProxy::~ColdProxy ()
{
if (layout ()) {
layout ()->unregister_cold_proxy (this);
}
delete mp_context_info;
mp_context_info = 0;
}
void
ColdProxy::unregister ()
{
if (layout ()) {
layout ()->unregister_cold_proxy (this);
}
}
void
ColdProxy::reregister ()
{
if (layout ()) {
layout ()->register_cold_proxy (this);
}
}
Cell *
ColdProxy::clone (Layout &layout) const
{

View File

@ -81,6 +81,16 @@ public:
return true;
}
/**
* @brief Reimplemented from Cell: unregisters the proxy at the layout
*/
virtual void unregister ();
/**
* @brief Reimplemented from Cell: reregisters the proxy at the layout
*/
virtual void reregister ();
/**
* @brief Gets a list of cold proxies for a given library name
*/

View File

@ -337,6 +337,37 @@ private:
// -----------------------------------------------------------------
// Implementation of the ProxyContextInfo class
bool
LayoutOrCellContextInfo::operator== (const LayoutOrCellContextInfo &other) const
{
return lib_name == other.lib_name &&
cell_name == other.cell_name &&
pcell_name == other.pcell_name &&
pcell_parameters == other.pcell_parameters &&
meta_info == other.meta_info;
}
bool
LayoutOrCellContextInfo::operator< (const LayoutOrCellContextInfo &other) const
{
if (lib_name != other.lib_name) {
return lib_name < other.lib_name;
}
if (cell_name != other.cell_name) {
return cell_name < other.cell_name;
}
if (pcell_name != other.pcell_name) {
return pcell_name < other.pcell_name;
}
if (pcell_parameters != other.pcell_parameters) {
return pcell_parameters < other.pcell_parameters;
}
if (meta_info != other.meta_info) {
return meta_info < other.meta_info;
}
return false;
}
LayoutOrCellContextInfo
LayoutOrCellContextInfo::deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to)
{
@ -540,6 +571,7 @@ Layout::clear ()
m_pcell_ids.clear ();
m_lib_proxy_map.clear ();
m_cold_proxy_map.clear ();
m_meta_info.clear ();
}
@ -568,6 +600,7 @@ Layout::operator= (const Layout &d)
}
m_lib_proxy_map = d.m_lib_proxy_map;
m_cold_proxy_map = d.m_cold_proxy_map;
m_cell_ptrs.resize (d.m_cell_ptrs.size (), 0);
@ -809,6 +842,7 @@ Layout::mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat
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_cold_proxy_map, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_meta_info, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_shape_repository, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_array_repository, true, (void *) this);
@ -1505,6 +1539,11 @@ Layout::register_cell_name (const char *name, cell_index_type ci)
void
Layout::rename_cell (cell_index_type id, const char *name)
{
static const char *anonymous_name = "";
if (! name) {
name = anonymous_name;
}
tl_assert (id < m_cell_names.size ());
if (strcmp (m_cell_names [id], name) != 0) {
@ -1521,7 +1560,10 @@ Layout::rename_cell (cell_index_type id, const char *name)
delete [] m_cell_names [id];
m_cell_names [id] = cp;
m_cell_map.insert (std::make_pair (cp, id));
// NOTE: anonymous cells (empty name string) are not registered in the cell name map
if (*cp != 0) {
m_cell_map.insert (std::make_pair (cp, id));
}
// to enforce a redraw and a rebuild
cell_name_changed ();
@ -1690,27 +1732,119 @@ Layout::cleanup (const std::set<db::cell_index_type> &keep)
return;
}
if (tl::verbosity () >= 30) {
tl::info << "Cleaning up layout ..";
}
// Do some polishing of the proxies - sometimes, specifically when resolving indirect library references,
// different proxies to the same library object exist. We can identify them and clean them up, so there
// is a single reference. We can also try to ensure that cell names reflect the library cell names.
// The latter is good for LVS for example.
{
db::LayoutLocker locker (this);
// join library proxies pointing to the same object
for (auto c = m_lib_proxy_map.begin (); c != m_lib_proxy_map.end (); ) {
auto c0 = c++;
size_t n = 1;
while (c != m_lib_proxy_map.end () && c->first == c0->first) {
++n;
++c;
}
if (n > 1) {
auto cc = c0;
++cc;
while (cc != c) {
if (keep.find (cc->second) == keep.end ()) {
if (tl::verbosity () >= 30) {
tl::info << "Joining lib proxy " << cell_name (cc->second) << " into " << cell_name (c0->second);
}
replace_instances_of (cc->second, c0->second);
}
++cc;
}
}
}
// join cold proxies pointing to the same object
for (auto c = m_cold_proxy_map.begin (); c != m_cold_proxy_map.end (); ) {
auto c0 = c++;
size_t n = 1;
while (c != m_cold_proxy_map.end () && c->first == c0->first) {
++n;
++c;
}
if (n > 1) {
auto cc = c0;
++cc;
while (cc != c) {
if (keep.find (cc->second) == keep.end ()) {
if (tl::verbosity () >= 30) {
tl::info << "Joining cold proxy " << cell_name (cc->second) << " into " << cell_name (c0->second);
}
replace_instances_of (cc->second, c0->second);
}
++cc;
}
}
}
}
std::set<cell_index_type> cells_to_delete;
// 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 ()) {
if (cell (*c).is_proxy () && keep.find (*c) == keep.end ()) {
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);
cells_to_delete.clear ();
}
// Try to ensure that cell names reflect the library cell names. The latter is good for LVS for example.
for (auto c = m_lib_proxy_map.begin (); c != m_lib_proxy_map.end (); ++c) {
std::string bn = cell (c->second).get_basic_name ();
if (bn != cell_name (c->second) && ! cell_by_name (bn.c_str ()).first) {
if (tl::verbosity () >= 30) {
tl::info << "Renaming lib proxy " << cell_name (c->second) << " to " << bn;
}
rename_cell (c->second, bn.c_str ());
}
}
for (auto c = m_cold_proxy_map.begin (); c != m_cold_proxy_map.end (); ++c) {
std::string bn = cell (c->second).get_basic_name ();
if (bn != cell_name (c->second) && ! cell_by_name (bn.c_str ()).first) {
if (tl::verbosity () >= 30) {
tl::info << "Renaming cold proxy " << cell_name (c->second) << " to " << bn;
}
rename_cell (c->second, bn.c_str ());
}
}
}
@ -3151,13 +3285,32 @@ Layout::variant_name (cell_index_type cell_index) const
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 ()));
auto key = std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ());
auto l = m_lib_proxy_map.find (key);
while (l != m_lib_proxy_map.end () && l->first == key) {
if (l->second == lib_proxy->Cell::cell_index ()) {
return;
}
++l;
}
m_lib_proxy_map.insert (std::make_pair (key, 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 ()));
auto key = std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ());
auto l = m_lib_proxy_map.find (key);
while (l != m_lib_proxy_map.end () && l->first == key) {
if (l->second == lib_proxy->Cell::cell_index ()) {
m_lib_proxy_map.erase (l);
break;
}
++l;
}
}
void
@ -3177,9 +3330,13 @@ Layout::get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_t
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 ()) {
auto key = std::make_pair (lib->get_id (), cell_index);
lib_proxy_map::const_iterator lp = m_lib_proxy_map.find (key);
if (lp != m_lib_proxy_map.end () && lp->first == key) {
return lp->second;
} else {
// create a new unique name
@ -3210,35 +3367,82 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index)
}
}
void
Layout::register_cold_proxy (db::ColdProxy *cold_proxy)
{
auto l = m_cold_proxy_map.find (cold_proxy->context_info ());
while (l != m_cold_proxy_map.end () && l->first == cold_proxy->context_info ()) {
if (l->second == cold_proxy->Cell::cell_index ()) {
return;
}
++l;
}
m_cold_proxy_map.insert (std::make_pair (cold_proxy->context_info (), cold_proxy->Cell::cell_index ()));
}
void
Layout::unregister_cold_proxy (db::ColdProxy *cold_proxy)
{
auto l = m_cold_proxy_map.find (cold_proxy->context_info ());
while (l != m_cold_proxy_map.end () && l->first == cold_proxy->context_info ()) {
if (l->second == cold_proxy->Cell::cell_index ()) {
m_cold_proxy_map.erase (l);
break;
}
++l;
}
}
std::pair<bool, cell_index_type>
Layout::find_cold_proxy (const db::LayoutOrCellContextInfo &info)
{
cold_proxy_map::const_iterator lp = m_cold_proxy_map.find (info);
if (lp != m_cold_proxy_map.end () && lp->first == info) {
return std::make_pair (true, lp->second);
} else {
return std::make_pair (false, 0);
}
}
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;
cold_proxy_map::const_iterator lp = m_cold_proxy_map.find (info);
if (lp != m_cold_proxy_map.end () && lp->first == info) {
return lp->second;
} else {
// 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;
}
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

View File

@ -62,6 +62,7 @@ class PCellDeclaration;
class PCellHeader;
class Library;
class LibraryProxy;
class ColdProxy;
class CellMapping;
class LayerMapping;
class Region;
@ -427,6 +428,9 @@ struct DB_PUBLIC LayoutOrCellContextInfo
std::map<std::string, tl::Variant> pcell_parameters;
std::map<std::string, std::pair<tl::Variant, std::string> > meta_info;
bool operator== (const LayoutOrCellContextInfo &other) const;
bool operator< (const LayoutOrCellContextInfo &other) const;
static LayoutOrCellContextInfo deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to);
void serialize (std::vector<std::string> &strings);
@ -469,7 +473,8 @@ public:
typedef db::pcell_id_type pcell_id_type;
typedef std::map<std::string, pcell_id_type> pcell_name_map;
typedef pcell_name_map::const_iterator pcell_iterator;
typedef std::map<std::pair<lib_id_type, cell_index_type>, cell_index_type> lib_proxy_map;
typedef std::multimap<std::pair<lib_id_type, cell_index_type>, cell_index_type> lib_proxy_map;
typedef std::multimap<db::LayoutOrCellContextInfo, cell_index_type> cold_proxy_map;
typedef LayerIterator layer_iterator;
typedef size_t meta_info_name_id_type;
typedef std::map<meta_info_name_id_type, MetaInfo> meta_info_map;
@ -1003,6 +1008,12 @@ public:
*/
void get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping = 0, bool retain_layout = false);
/**
* @brief Find an existing cold proxy for a given context
* @return A pair of success flag and cell index of the proxy
*/
std::pair<bool, cell_index_type> find_cold_proxy (const db::LayoutOrCellContextInfo &info);
/**
* @brief Creates a cold proxy representing the given context information
*/
@ -1839,6 +1850,20 @@ public:
*/
void unregister_lib_proxy (db::LibraryProxy *lib_proxy);
/**
* @brief Register a cold proxy
*
* This method is used by ColdProxy to register itself.
*/
void register_cold_proxy (db::ColdProxy *cold_proxy);
/**
* @brief Unregister a cold proxy
*
* This method is used by ColdProxy to unregister itself.
*/
void unregister_cold_proxy (db::ColdProxy *cold_proxy);
/**
* @brief Gets the editable status of this layout
*
@ -2153,6 +2178,7 @@ private:
std::vector<pcell_header_type *> m_pcells;
pcell_name_map m_pcell_ids;
lib_proxy_map m_lib_proxy_map;
cold_proxy_map m_cold_proxy_map;
bool m_do_cleanup;
bool m_editable;
std::map<std::string, meta_info_name_id_type> m_meta_info_name_map;

View File

@ -221,14 +221,25 @@ LibraryProxy::update (db::ImportLayerMapping *layer_mapping)
real_lib = db::LibraryManager::instance ().lib (lp->lib_id ());
}
inst.object ().cell_index (layout ()->get_lib_proxy (real_lib, real_cil));
ColdProxy *cp = dynamic_cast<ColdProxy *> (&real_lib->layout ().cell (real_cil));
if (cp) {
// The final item is a cold proxy ("<defunct>" cell) - treat it like
// a library proxy as it may become one in the future, but replace it
// by a cold proxy now.
layout ()->create_cold_proxy_as (cp->context_info (), inst.object ().cell_index ());
auto p = layout ()->find_cold_proxy (cp->context_info ());
if (p.first) {
// reuse existing proxy
inst.object ().cell_index (p.second);
} else {
// create a new proxy, reusing the first library proxy's replica
inst.object ().cell_index (layout ()->get_lib_proxy (real_lib, real_cil));
layout ()->create_cold_proxy_as (cp->context_info (), inst.object ().cell_index ());
}
} else {
inst.object ().cell_index (layout ()->get_lib_proxy (real_lib, real_cil));
}
inst.transform_into (db::ICplxTrans (lib->layout ().dbu () / layout ()->dbu ()));

View File

@ -129,12 +129,12 @@ public:
/**
* @brief Reimplemented from Cell: unregisters the proxy at the layout
*/
void unregister ();
virtual void unregister ();
/**
* @brief Reimplemented from Cell: reregisters the proxy at the layout
*/
void reregister ();
virtual void reregister ();
private:
lib_id_type m_lib_id;

View File

@ -773,8 +773,8 @@ TEST(7_monsterlib)
}
// as NOEX and NOEX2 are not present, a number of references are defunct (aka cold proxies)
EXPECT_EQ (num_defunct (layout), size_t (15));
EXPECT_EQ (num_cells (layout), size_t (46));
EXPECT_EQ (num_defunct (layout), size_t (6));
EXPECT_EQ (num_cells (layout), size_t (25));
EXPECT_EQ (num_top_cells (layout), size_t (1));
// NOTE: normalization would spoil the layout, so don't do it
@ -794,7 +794,7 @@ TEST(7_monsterlib)
// all references now need to be resolved
EXPECT_EQ (num_defunct (layout), size_t (0));
EXPECT_EQ (num_cells (layout), size_t (36));
EXPECT_EQ (num_cells (layout), size_t (25));
EXPECT_EQ (num_top_cells (layout), size_t (1));
db::compare_layouts (_this, layout, tl::testsrc () + "/testdata/libman/design_au2.gds", db::NormalizationMode (db::NoNormalization | db::WithoutCellNames | db::AsPolygons));
@ -807,7 +807,7 @@ TEST(7_monsterlib)
// all references now need to be resolved
EXPECT_EQ (num_defunct (layout), size_t (0));
EXPECT_EQ (num_cells (layout), size_t (32));
EXPECT_EQ (num_cells (layout), size_t (25));
EXPECT_EQ (num_top_cells (layout), size_t (1));
db::compare_layouts (_this, layout, tl::testsrc () + "/testdata/libman/design_au3.gds", db::NormalizationMode (db::NoNormalization | db::WithoutCellNames | db::AsPolygons));
@ -816,8 +816,8 @@ TEST(7_monsterlib)
db::LibraryManager::instance ().delete_lib (lib_noex2);
// after removing the libraries, we have defunct cells again
EXPECT_EQ (num_defunct (layout), size_t (11));
EXPECT_EQ (num_cells (layout), size_t (32));
EXPECT_EQ (num_defunct (layout), size_t (6));
EXPECT_EQ (num_cells (layout), size_t (25));
EXPECT_EQ (num_top_cells (layout), size_t (1));
// but the layout did not change
@ -827,8 +827,8 @@ TEST(7_monsterlib)
db::LibraryManager::instance ().delete_lib (lib_ex2);
// after removing all libraries, we have even more defunct cells (i.e. all, except top)
EXPECT_EQ (num_defunct (layout), size_t (19));
EXPECT_EQ (num_cells (layout), size_t (32));
EXPECT_EQ (num_defunct (layout), size_t (12));
EXPECT_EQ (num_cells (layout), size_t (25));
EXPECT_EQ (num_top_cells (layout), size_t (1));
// but the layout did not change

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.