diff --git a/src/db/db/dbFileBasedLibrary.cc b/src/db/db/dbFileBasedLibrary.cc index 63086fda1..10025e650 100644 --- a/src/db/db/dbFileBasedLibrary.cc +++ b/src/db/db/dbFileBasedLibrary.cc @@ -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 diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 40557f375..9411dcef3 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -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 cold_proxies; @@ -3004,9 +3012,7 @@ Layout::restore_proxies (ImportLayerMapping *layer_mapping) } } - if (needs_cleanup) { - cleanup (); - } + return needs_cleanup; } bool diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index ed62a60ab..e68b5746c 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -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 diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index e3e4c353e..fd457ba57 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -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 > referrers; + std::vector referrers; for (std::map::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 needs_cleanup; - for (std::vector >::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 > pcells_to_map; - std::vector lib_cells_to_map; + any = false; - for (auto c = r->first->begin (); c != r->first->end (); ++c) { + for (std::vector::const_iterator r = referrers.begin (); r != referrers.end (); ++r) { - db::LibraryProxy *lib_proxy = dynamic_cast (c.operator-> ()); - if (lib_proxy && lib_proxy->lib_id () == get_id ()) { + std::vector > pcells_to_map; + std::vector lib_cells_to_map; + + for (auto c = (*r)->begin (); c != (*r)->end (); ++c) { + + db::LibraryProxy *lib_proxy = dynamic_cast (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 (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 (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 >::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 >::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 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 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::const_iterator lp = lib_cells_to_map.begin (); lp != lib_cells_to_map.end (); ++lp) { - for (std::vector::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 cn (false, 0); + if (other) { + cn = other->layout ().cell_by_name (original_layout->cell_name ((*lp)->library_cell_index ())); + } - std::pair 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); + } } diff --git a/src/db/db/dbLibraryManager.cc b/src/db/db/dbLibraryManager.cc index 304c27e9f..b1d837985 100644 --- a/src/db/db/dbLibraryManager.cc +++ b/src/db/db/dbLibraryManager.cc @@ -249,13 +249,38 @@ LibraryManager::register_lib (Library *library) // "restore_proxies" takes care not to re-substitute cold proxies. const tl::weak_collection &cold_proxies = db::ColdProxy::cold_proxies_per_lib_name (library->get_name ()); - std::set to_refresh; + + std::set to_refresh_set; for (tl::weak_collection::const_iterator p = cold_proxies.begin (); p != cold_proxies.end (); ++p) { - to_refresh.insert (const_cast (p->layout ())); + to_refresh_set.insert (const_cast (p->layout ())); } - for (std::set::const_iterator l = to_refresh.begin (); l != to_refresh.end (); ++l) { - (*l)->restore_proxies (0); + // Sort for deterministic order of resolution + std::vector to_refresh (to_refresh_set.begin (), to_refresh_set.end ()); + std::sort (to_refresh.begin (), to_refresh.end (), tl::sort_by_id ()); + + std::set 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 diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index de3f9789d..711eb8f2d 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -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); } diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index 368c1bad8..df013f8a1 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -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 }; /** diff --git a/src/db/unit_tests/dbLibrariesTests.cc b/src/db/unit_tests/dbLibrariesTests.cc index 1d48e852e..bbe74b65d 100644 --- a/src/db/unit_tests/dbLibrariesTests.cc +++ b/src/db/unit_tests/dbLibrariesTests.cc @@ -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 (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 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)); +} diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index e507b7e93..227396c48 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -289,22 +289,30 @@ LibraryController::sync_files () } for (std::map::const_iterator lf = m_lib_files.begin (); lf != m_lib_files.end (); ++lf) { + + std::pair 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 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 diff --git a/src/tl/tl/tlUniqueId.h b/src/tl/tl/tlUniqueId.h index 54bf1f9d7..8d065a8af 100644 --- a/src/tl/tl/tlUniqueId.h +++ b/src/tl/tl/tlUniqueId.h @@ -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 diff --git a/testdata/libman/design.gds b/testdata/libman/design.gds new file mode 100644 index 000000000..452c0f56d Binary files /dev/null and b/testdata/libman/design.gds differ diff --git a/testdata/libman/design_au1.gds b/testdata/libman/design_au1.gds new file mode 100644 index 000000000..5419c7754 Binary files /dev/null and b/testdata/libman/design_au1.gds differ diff --git a/testdata/libman/design_au2.gds b/testdata/libman/design_au2.gds new file mode 100644 index 000000000..c3e3d4925 Binary files /dev/null and b/testdata/libman/design_au2.gds differ diff --git a/testdata/libman/design_au3.gds b/testdata/libman/design_au3.gds new file mode 100644 index 000000000..10b4c7ba8 Binary files /dev/null and b/testdata/libman/design_au3.gds differ diff --git a/testdata/libman/design_au4.gds b/testdata/libman/design_au4.gds new file mode 100644 index 000000000..ee9e07f25 Binary files /dev/null and b/testdata/libman/design_au4.gds differ diff --git a/testdata/libman/design_au5.gds b/testdata/libman/design_au5.gds new file mode 100644 index 000000000..9f28c5213 Binary files /dev/null and b/testdata/libman/design_au5.gds differ diff --git a/testdata/libman/libs/EX.gds b/testdata/libman/libs/EX.gds new file mode 100644 index 000000000..013c53ad1 Binary files /dev/null and b/testdata/libman/libs/EX.gds differ diff --git a/testdata/libman/libs/EX2.gds b/testdata/libman/libs/EX2.gds new file mode 100644 index 000000000..fcaae5c0a Binary files /dev/null and b/testdata/libman/libs/EX2.gds differ diff --git a/testdata/libman/libs/NOEX.gds b/testdata/libman/libs/NOEX.gds new file mode 100644 index 000000000..b1235b830 Binary files /dev/null and b/testdata/libman/libs/NOEX.gds differ diff --git a/testdata/libman/libs/NOEX2.gds b/testdata/libman/libs/NOEX2.gds new file mode 100644 index 000000000..1d86242b2 Binary files /dev/null and b/testdata/libman/libs/NOEX2.gds differ