mirror of https://github.com/KLayout/klayout.git
Taking care of complex library reference scenarios where libraries self-reference and under the presence of stale references. Main issue is order of resolution and indirect references due to replication. Solution is to repeat resolution until saturated. A test is supplied.
This commit is contained in:
parent
062567f206
commit
7cec679d39
|
|
@ -37,6 +37,11 @@ FileBasedLibrary::FileBasedLibrary (const std::string &path, const std::string &
|
|||
: db::Library (), m_name (name), m_path (path), m_is_loaded (false)
|
||||
{
|
||||
set_description (tl::filename (path));
|
||||
|
||||
// preliminary name, may be replaced later
|
||||
if (! name.empty ()) {
|
||||
set_name (name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -2987,6 +2987,14 @@ Layout::fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrC
|
|||
|
||||
void
|
||||
Layout::restore_proxies (ImportLayerMapping *layer_mapping)
|
||||
{
|
||||
if (restore_proxies_without_cleanup (layer_mapping)) {
|
||||
cleanup ();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Layout::restore_proxies_without_cleanup (ImportLayerMapping *layer_mapping)
|
||||
{
|
||||
std::vector<db::ColdProxy *> cold_proxies;
|
||||
|
||||
|
|
@ -3004,9 +3012,7 @@ Layout::restore_proxies (ImportLayerMapping *layer_mapping)
|
|||
}
|
||||
}
|
||||
|
||||
if (needs_cleanup) {
|
||||
cleanup ();
|
||||
}
|
||||
return needs_cleanup;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -1115,7 +1115,16 @@ public:
|
|||
* Library updates may enabled lost connections which are help in cold proxies. This method will recover
|
||||
* these connections.
|
||||
*/
|
||||
void restore_proxies(ImportLayerMapping *layer_mapping = 0);
|
||||
void restore_proxies (ImportLayerMapping *layer_mapping = 0);
|
||||
|
||||
/**
|
||||
* @brief Restores proxies as far as possible, no cleanup included
|
||||
*
|
||||
* This method is equivalent to "restore_proxies", but does not include a cleanup.
|
||||
* Instead it returns a value of true, indicating that something got changed
|
||||
* and a cleanup is required.
|
||||
*/
|
||||
bool restore_proxies_without_cleanup (ImportLayerMapping *layer_mapping = 0);
|
||||
|
||||
/**
|
||||
* @brief Replaces the given cell index with the new cell
|
||||
|
|
|
|||
|
|
@ -208,110 +208,128 @@ Library::remap_to (db::Library *other, db::Layout *original_layout)
|
|||
|
||||
// Hint: in the loop over the referrers we might unregister (delete from m_referrers) a referrer because no more cells refer to us.
|
||||
// Hence we must not directly iterate of m_referrers.
|
||||
std::vector<std::pair<db::Layout *, int> > referrers;
|
||||
std::vector<db::Layout *> referrers;
|
||||
for (std::map<db::Layout *, int>::const_iterator r = m_referrers.begin (); r != m_referrers.end (); ++r) {
|
||||
referrers.push_back (*r);
|
||||
referrers.push_back (r->first);
|
||||
}
|
||||
|
||||
// Sort for deterministic order of resolution
|
||||
std::sort (referrers.begin (), referrers.end (), tl::sort_by_id ());
|
||||
|
||||
// Remember the layouts that will finally need a cleanup
|
||||
std::set<db::Layout *> needs_cleanup;
|
||||
|
||||
for (std::vector<std::pair<db::Layout *, int> >::const_iterator r = referrers.begin (); r != referrers.end (); ++r) {
|
||||
// NOTE: resolution may create new references due to replicas.
|
||||
// Hence, loop until no further references are resolved.
|
||||
bool any = true;
|
||||
while (any) {
|
||||
|
||||
std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> > pcells_to_map;
|
||||
std::vector<db::LibraryProxy *> lib_cells_to_map;
|
||||
any = false;
|
||||
|
||||
for (auto c = r->first->begin (); c != r->first->end (); ++c) {
|
||||
for (std::vector<db::Layout *>::const_iterator r = referrers.begin (); r != referrers.end (); ++r) {
|
||||
|
||||
db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (c.operator-> ());
|
||||
if (lib_proxy && lib_proxy->lib_id () == get_id ()) {
|
||||
std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> > pcells_to_map;
|
||||
std::vector<db::LibraryProxy *> lib_cells_to_map;
|
||||
|
||||
for (auto c = (*r)->begin (); c != (*r)->end (); ++c) {
|
||||
|
||||
db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (c.operator-> ());
|
||||
if (lib_proxy && lib_proxy->lib_id () == get_id ()) {
|
||||
|
||||
if (! original_layout->is_valid_cell_index (lib_proxy->library_cell_index ())) {
|
||||
// safety feature, should not happen
|
||||
continue;
|
||||
}
|
||||
|
||||
db::Cell *lib_cell = &original_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.insert (*r);
|
||||
any = true;
|
||||
|
||||
db::Cell *lib_cell = &original_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.insert (r->first);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// 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) {
|
||||
|
||||
// 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;
|
||||
|
||||
db::cell_index_type ci = lp->first->Cell::cell_index ();
|
||||
db::PCellVariant *lib_pcell = lp->second;
|
||||
std::pair<bool, pcell_id_type> pn (false, 0);
|
||||
if (other) {
|
||||
pn = other->layout ().pcell_by_name (original_layout->cell (lp->first->library_cell_index ()).get_basic_name ().c_str ());
|
||||
}
|
||||
|
||||
std::pair<bool, pcell_id_type> pn (false, 0);
|
||||
if (other) {
|
||||
pn = other->layout ().pcell_by_name (original_layout->cell (lp->first->library_cell_index ()).get_basic_name ().c_str ());
|
||||
}
|
||||
|
||||
if (! pn.first) {
|
||||
|
||||
// substitute by a cold proxy
|
||||
db::LayoutOrCellContextInfo info;
|
||||
r->first->get_context_info (ci, info);
|
||||
r->first->create_cold_proxy_as (info, ci);
|
||||
|
||||
} else {
|
||||
|
||||
const db::PCellDeclaration *old_pcell_decl = original_layout->pcell_declaration (lib_pcell->pcell_id ());
|
||||
const db::PCellDeclaration *new_pcell_decl = other->layout ().pcell_declaration (pn.second);
|
||||
if (! old_pcell_decl || ! new_pcell_decl) {
|
||||
if (! pn.first) {
|
||||
|
||||
// substitute by a cold proxy
|
||||
db::LayoutOrCellContextInfo info;
|
||||
r->first->get_context_info (ci, info);
|
||||
r->first->create_cold_proxy_as (info, ci);
|
||||
(*r)->get_context_info (ci, info);
|
||||
(*r)->create_cold_proxy_as (info, ci);
|
||||
|
||||
} else {
|
||||
|
||||
db::pcell_parameters_type new_parameters = new_pcell_decl->map_parameters (lib_pcell->parameters_by_name ());
|
||||
const db::PCellDeclaration *old_pcell_decl = original_layout->pcell_declaration (lib_pcell->pcell_id ());
|
||||
const db::PCellDeclaration *new_pcell_decl = other->layout ().pcell_declaration (pn.second);
|
||||
if (! old_pcell_decl || ! new_pcell_decl) {
|
||||
|
||||
// substitute by a cold proxy
|
||||
db::LayoutOrCellContextInfo info;
|
||||
(*r)->get_context_info (ci, info);
|
||||
(*r)->create_cold_proxy_as (info, ci);
|
||||
|
||||
} else {
|
||||
|
||||
db::pcell_parameters_type new_parameters = new_pcell_decl->map_parameters (lib_pcell->parameters_by_name ());
|
||||
|
||||
// coerce the new parameters if requested
|
||||
try {
|
||||
db::pcell_parameters_type plist = new_parameters;
|
||||
new_pcell_decl->coerce_parameters (other->layout (), plist);
|
||||
plist.swap (new_parameters);
|
||||
} catch (tl::Exception &ex) {
|
||||
// ignore exception - we will do that again on update() to establish an error message
|
||||
tl::error << ex.msg ();
|
||||
}
|
||||
|
||||
lp->first->remap (other->get_id (), other->layout ().get_pcell_variant (pn.second, new_parameters));
|
||||
|
||||
// coerce the new parameters if requested
|
||||
try {
|
||||
db::pcell_parameters_type plist = new_parameters;
|
||||
new_pcell_decl->coerce_parameters (other->layout (), plist);
|
||||
plist.swap (new_parameters);
|
||||
} catch (tl::Exception &ex) {
|
||||
// ignore exception - we will do that again on update() to establish an error message
|
||||
tl::error << ex.msg ();
|
||||
}
|
||||
|
||||
lp->first->remap (other->get_id (), other->layout ().get_pcell_variant (pn.second, new_parameters));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
for (std::vector<db::LibraryProxy *>::const_iterator lp = lib_cells_to_map.begin (); lp != lib_cells_to_map.end (); ++lp) {
|
||||
|
||||
for (std::vector<db::LibraryProxy *>::const_iterator lp = lib_cells_to_map.begin (); lp != lib_cells_to_map.end (); ++lp) {
|
||||
db::cell_index_type ci = (*lp)->Cell::cell_index ();
|
||||
|
||||
db::cell_index_type ci = (*lp)->Cell::cell_index ();
|
||||
std::pair<bool, cell_index_type> cn (false, 0);
|
||||
if (other) {
|
||||
cn = other->layout ().cell_by_name (original_layout->cell_name ((*lp)->library_cell_index ()));
|
||||
}
|
||||
|
||||
std::pair<bool, cell_index_type> cn (false, 0);
|
||||
if (other) {
|
||||
cn = other->layout ().cell_by_name (original_layout->cell_name ((*lp)->library_cell_index ()));
|
||||
}
|
||||
if (! cn.first) {
|
||||
|
||||
if (! cn.first) {
|
||||
// substitute by a cold proxy
|
||||
db::LayoutOrCellContextInfo info;
|
||||
(*r)->get_context_info (ci, info);
|
||||
(*r)->create_cold_proxy_as (info, ci);
|
||||
|
||||
// substitute by a cold proxy
|
||||
db::LayoutOrCellContextInfo info;
|
||||
r->first->get_context_info (ci, info);
|
||||
r->first->create_cold_proxy_as (info, ci);
|
||||
} else {
|
||||
|
||||
} else {
|
||||
(*lp)->remap (other->get_id (), cn.second);
|
||||
|
||||
(*lp)->remap (other->get_id (), cn.second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -249,13 +249,38 @@ LibraryManager::register_lib (Library *library)
|
|||
// "restore_proxies" takes care not to re-substitute cold proxies.
|
||||
|
||||
const tl::weak_collection<db::ColdProxy> &cold_proxies = db::ColdProxy::cold_proxies_per_lib_name (library->get_name ());
|
||||
std::set<db::Layout *> to_refresh;
|
||||
|
||||
std::set<db::Layout *> to_refresh_set;
|
||||
for (tl::weak_collection<db::ColdProxy>::const_iterator p = cold_proxies.begin (); p != cold_proxies.end (); ++p) {
|
||||
to_refresh.insert (const_cast<db::Layout *> (p->layout ()));
|
||||
to_refresh_set.insert (const_cast<db::Layout *> (p->layout ()));
|
||||
}
|
||||
|
||||
for (std::set<db::Layout *>::const_iterator l = to_refresh.begin (); l != to_refresh.end (); ++l) {
|
||||
(*l)->restore_proxies (0);
|
||||
// Sort for deterministic order of resolution
|
||||
std::vector<db::Layout *> to_refresh (to_refresh_set.begin (), to_refresh_set.end ());
|
||||
std::sort (to_refresh.begin (), to_refresh.end (), tl::sort_by_id ());
|
||||
|
||||
std::set<db::Layout *> needs_cleanup;
|
||||
|
||||
// NOTE: "restore proxies" can create new proxies, because indirect references.
|
||||
// Hence we need to repeat the process.
|
||||
bool any = true;
|
||||
while (any) {
|
||||
|
||||
any = false;
|
||||
|
||||
for (auto l = to_refresh.begin (); l != to_refresh.end (); ++l) {
|
||||
if ((*l)->restore_proxies_without_cleanup ()) {
|
||||
any = true;
|
||||
needs_cleanup.insert (*l);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// do the cleanup
|
||||
for (auto l = needs_cleanup.begin (); l != needs_cleanup.end (); ++l) {
|
||||
(*l)->cleanup ();
|
||||
}
|
||||
|
||||
// issue the change notification
|
||||
|
|
|
|||
|
|
@ -135,13 +135,52 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std::
|
|||
db::Reader reader (stream);
|
||||
reader.read (layout_au, options);
|
||||
|
||||
equal = db::compare_layouts (*subject, layout_au,
|
||||
(n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose)
|
||||
| ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0)
|
||||
| ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts)
|
||||
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta)
|
||||
/*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/
|
||||
, tolerance, 100 /*max diff lines*/);
|
||||
if ((norm & WithoutCellNames) != 0) {
|
||||
|
||||
// in this case, the layouts need to have one top cell
|
||||
|
||||
db::cell_index_type top_subject = 0, top_au = 0;
|
||||
size_t n_top_subject = 0, n_top_au = 0;
|
||||
|
||||
for (auto t = subject->begin_top_down (); t != subject->end_top_cells (); ++t) {
|
||||
top_subject = *t;
|
||||
++n_top_subject;
|
||||
}
|
||||
|
||||
for (auto t = layout_au.begin_top_down (); t != layout_au.end_top_cells (); ++t) {
|
||||
top_au = *t;
|
||||
++n_top_au;
|
||||
}
|
||||
|
||||
if (n_top_subject != 1) {
|
||||
throw tl::Exception (tl::sprintf ("With smart cell mapping, the subject layout must have a single top cell"));
|
||||
}
|
||||
if (n_top_au != 1) {
|
||||
throw tl::Exception (tl::sprintf ("With smart cell mapping, the reference layout must have a single top cell"));
|
||||
}
|
||||
|
||||
equal = db::compare_layouts (*subject, top_subject, layout_au, top_au,
|
||||
(n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose)
|
||||
| ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0)
|
||||
| ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts)
|
||||
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta
|
||||
| db::layout_diff::f_smart_cell_mapping)
|
||||
/*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/
|
||||
, tolerance, 100 /*max diff lines*/);
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
equal = db::compare_layouts (*subject, layout_au,
|
||||
(n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose)
|
||||
| ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0)
|
||||
| ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts)
|
||||
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta)
|
||||
/*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/
|
||||
, tolerance, 100 /*max diff lines*/);
|
||||
|
||||
}
|
||||
|
||||
if (equal && n > 0) {
|
||||
tl::info << tl::sprintf ("Found match on golden reference variant %s", fn);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ enum NormalizationMode
|
|||
NoContext = 8, // write tmp file without context
|
||||
AsPolygons = 16, // paths and boxes are treated as polygons
|
||||
WithArrays = 32, // do not flatten arrays
|
||||
WithMeta = 64 // with meta info
|
||||
WithMeta = 64, // with meta info
|
||||
WithoutCellNames = 128 // smart cell name mapping
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
#include "dbReader.h"
|
||||
#include "dbLayoutDiff.h"
|
||||
#include "dbTestSupport.h"
|
||||
#include "dbFileBasedLibrary.h"
|
||||
#include "dbColdProxy.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlStaticObjects.h"
|
||||
#include "tlUnitTest.h"
|
||||
|
|
@ -698,3 +700,137 @@ TEST(6_issue996)
|
|||
CHECKPOINT ();
|
||||
db::compare_layouts (this, ly, tl::testdata () + "/gds/lib_test6b.gds", db::NormalizationMode (db::WriteGDS2 + db::NoContext));
|
||||
}
|
||||
|
||||
static size_t num_top_cells (const db::Layout &layout)
|
||||
{
|
||||
size_t n = 0;
|
||||
for (auto t = layout.begin_top_down (); t != layout.end_top_cells (); ++t) {
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static size_t num_cells (const db::Layout &layout)
|
||||
{
|
||||
size_t n = 0;
|
||||
for (auto t = layout.begin_top_down (); t != layout.end_top_down (); ++t) {
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static size_t num_defunct (const db::Layout &layout)
|
||||
{
|
||||
size_t ndefunct = 0;
|
||||
for (auto c = layout.begin (); c != layout.end (); ++c) {
|
||||
if (dynamic_cast<const db::ColdProxy *> (c.operator-> ())) {
|
||||
++ndefunct;
|
||||
}
|
||||
}
|
||||
return ndefunct;
|
||||
}
|
||||
|
||||
// monster lib refresh issue
|
||||
// (monster lib is a layout with manifold library references to existing and non-existing libraries)
|
||||
TEST(7_monsterlib)
|
||||
{
|
||||
std::pair<bool, db::lib_id_type> lib;
|
||||
|
||||
// tabula rasa
|
||||
lib = db::LibraryManager::instance ().lib_by_name ("EX");
|
||||
if (lib.first) {
|
||||
db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (lib.second));
|
||||
}
|
||||
lib = db::LibraryManager::instance ().lib_by_name ("EX2");
|
||||
if (lib.first) {
|
||||
db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (lib.second));
|
||||
}
|
||||
lib = db::LibraryManager::instance ().lib_by_name ("NOEX");
|
||||
if (lib.first) {
|
||||
db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (lib.second));
|
||||
}
|
||||
lib = db::LibraryManager::instance ().lib_by_name ("NOEX2");
|
||||
if (lib.first) {
|
||||
db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (lib.second));
|
||||
}
|
||||
|
||||
// first, read the layout with only EX and EX2 in place
|
||||
|
||||
db::FileBasedLibrary *lib_ex = new db::FileBasedLibrary (tl::testsrc () + "/testdata/libman/libs/EX.gds", "EX");
|
||||
lib_ex->load ();
|
||||
db::LibraryManager::instance ().register_lib (lib_ex);
|
||||
db::FileBasedLibrary *lib_ex2 = new db::FileBasedLibrary (tl::testsrc () + "/testdata/libman/libs/EX2.gds", "EX2");
|
||||
lib_ex2->load ();
|
||||
db::LibraryManager::instance ().register_lib (lib_ex2);
|
||||
|
||||
db::Layout layout;
|
||||
layout.do_cleanup (true);
|
||||
|
||||
{
|
||||
tl::InputStream is (tl::testsrc () + "/testdata/libman/design.gds");
|
||||
db::Reader reader (is);
|
||||
reader.read (layout);
|
||||
}
|
||||
|
||||
// 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_top_cells (layout), size_t (1));
|
||||
|
||||
// NOTE: normalization would spoil the layout, so don't do it
|
||||
// The golden layout is a spoiled version that uses preliminary cell versions for the
|
||||
// unresolved references of NOEX and NOEX2. This is intentional to test the replication.
|
||||
// Also note, that the golden file has static cells.
|
||||
db::compare_layouts (_this, layout, tl::testsrc () + "/testdata/libman/design_au1.gds", db::NormalizationMode (db::NoNormalization | db::WithoutCellNames | db::AsPolygons));
|
||||
|
||||
// then, establish NOEX and NOEX2 too - this will update the libraries in the layout that was read
|
||||
|
||||
db::FileBasedLibrary *lib_noex = new db::FileBasedLibrary (tl::testsrc () + "/testdata/libman/libs/NOEX.gds", "NOEX");
|
||||
lib_noex->load ();
|
||||
db::LibraryManager::instance ().register_lib (lib_noex);
|
||||
db::FileBasedLibrary *lib_noex2 = new db::FileBasedLibrary (tl::testsrc () + "/testdata/libman/libs/NOEX2.gds", "NOEX2");
|
||||
lib_noex2->load ();
|
||||
db::LibraryManager::instance ().register_lib (lib_noex2);
|
||||
|
||||
// all references now need to be resolved
|
||||
EXPECT_EQ (num_defunct (layout), size_t (0));
|
||||
EXPECT_EQ (num_cells (layout), size_t (34));
|
||||
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));
|
||||
|
||||
// refresh must not change the layout
|
||||
lib_ex->refresh ();
|
||||
lib_ex2->refresh ();
|
||||
lib_noex->refresh ();
|
||||
lib_noex2->refresh ();
|
||||
|
||||
// all references now need to be resolved
|
||||
EXPECT_EQ (num_defunct (layout), size_t (0));
|
||||
EXPECT_EQ (num_cells (layout), size_t (29));
|
||||
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));
|
||||
|
||||
db::LibraryManager::instance ().delete_lib (lib_noex);
|
||||
db::LibraryManager::instance ().delete_lib (lib_noex2);
|
||||
|
||||
// after removing the libraries, we have defunct cells again
|
||||
EXPECT_EQ (num_defunct (layout), size_t (8));
|
||||
EXPECT_EQ (num_cells (layout), size_t (29));
|
||||
EXPECT_EQ (num_top_cells (layout), size_t (1));
|
||||
|
||||
// but the layout did not change
|
||||
db::compare_layouts (_this, layout, tl::testsrc () + "/testdata/libman/design_au4.gds", db::NormalizationMode (db::NoNormalization | db::WithoutCellNames | db::AsPolygons));
|
||||
|
||||
db::LibraryManager::instance ().delete_lib (lib_ex);
|
||||
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 (16));
|
||||
EXPECT_EQ (num_cells (layout), size_t (29));
|
||||
EXPECT_EQ (num_top_cells (layout), size_t (1));
|
||||
|
||||
// but the layout did not change
|
||||
db::compare_layouts (_this, layout, tl::testsrc () + "/testdata/libman/design_au5.gds", db::NormalizationMode (db::NoNormalization | db::WithoutCellNames | db::AsPolygons));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,22 +289,30 @@ LibraryController::sync_files ()
|
|||
}
|
||||
|
||||
for (std::map<std::string, LibInfo>::const_iterator lf = m_lib_files.begin (); lf != m_lib_files.end (); ++lf) {
|
||||
|
||||
std::pair<bool, db::lib_id_type> li = db::LibraryManager::instance ().lib_by_name (lf->second.name, lf->second.tech);
|
||||
if (! li.first) {
|
||||
continue; // should not happen
|
||||
}
|
||||
|
||||
db::Library *lib = db::LibraryManager::instance ().lib (li.second);
|
||||
|
||||
if (new_names.find (lf->second.name) == new_names.end ()) {
|
||||
|
||||
try {
|
||||
std::pair<bool, db::lib_id_type> li = db::LibraryManager::instance ().lib_by_name (lf->second.name, lf->second.tech);
|
||||
if (li.first) {
|
||||
if (! lf->second.tech.empty ()) {
|
||||
tl::log << "Unregistering lib '" << lf->second.name << "' for technology '" << *lf->second.tech.begin () << "' as the file no longer exists: " << lf->first;
|
||||
} else {
|
||||
tl::log << "Unregistering lib '" << lf->second.name << "' as the file no longer exists: " << lf->first;
|
||||
}
|
||||
db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (li.second));
|
||||
if (! lf->second.tech.empty ()) {
|
||||
tl::log << "Unregistering lib '" << lf->second.name << "' for technology '" << *lf->second.tech.begin () << "' as the file no longer exists: " << lf->first;
|
||||
} else {
|
||||
tl::log << "Unregistering lib '" << lf->second.name << "' as the file no longer exists: " << lf->first;
|
||||
}
|
||||
db::LibraryManager::instance ().delete_lib (lib);
|
||||
} catch (tl::Exception &ex) {
|
||||
tl::error << ex.msg ();
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// establish the new libraries
|
||||
|
|
|
|||
|
|
@ -75,6 +75,17 @@ inline id_type id_of (const UniqueId *o)
|
|||
return o ? o->m_id : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A sorting operator of pointers by ID
|
||||
*/
|
||||
struct sort_by_id
|
||||
{
|
||||
bool operator () (const UniqueId *a, const UniqueId *b) const
|
||||
{
|
||||
return id_of (a) < id_of (b);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace tl
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue