diff --git a/src/db/db/dbColdProxy.cc b/src/db/db/dbColdProxy.cc index a387f2838..33346ef91 100644 --- a/src/db/db/dbColdProxy.cc +++ b/src/db/db/dbColdProxy.cc @@ -77,9 +77,9 @@ std::string ColdProxy::get_basic_name () const { if (! mp_context_info->pcell_name.empty ()) { - return "" + mp_context_info->pcell_name; + return "" + mp_context_info->pcell_name; } else if (! mp_context_info->cell_name.empty ()) { - return "" + mp_context_info->cell_name; + return "" + mp_context_info->cell_name; } else { return Cell::get_basic_name (); } diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index d627ac812..6ff5baecf 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -86,6 +86,27 @@ 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 { @@ -509,6 +530,18 @@ Layout::technology () const return db::Technologies::instance ()->technology_by_name (m_tech_name); } +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) { @@ -666,7 +699,7 @@ Layout::set_technology_name (const std::string &tech) } - m_tech_name = tech; + 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 (); @@ -2043,9 +2076,20 @@ Layout::replace_cell (cell_index_type target_cell_index, db::Cell *new_cell, boo } } - m_cells.erase (iterator (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 @@ -2060,7 +2104,6 @@ Layout::get_pcell_variant_as (pcell_id_type pcell_id, const std::vectorget_variant (*this, parameters) == 0); - tl_assert (! (manager () && manager ()->transacting ())); tl_assert (m_cell_ptrs [target_cell_index] != 0); pcell_variant_type *variant = new pcell_variant_type (target_cell_index, *this, pcell_id, parameters); @@ -2628,7 +2671,6 @@ Layout::unregister_lib_proxy (db::LibraryProxy *lib_proxy) 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 (! (manager () && manager ()->transacting ())); tl_assert (m_cell_ptrs [target_cell_index] != 0); LibraryProxy *proxy = new LibraryProxy (target_cell_index, *this, lib->get_id (), cell_index); @@ -2710,7 +2752,6 @@ Layout::create_cold_proxy (const db::ProxyContextInfo &info) void Layout::create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type target_cell_index) { - tl_assert (! (manager () && manager ()->transacting ())); tl_assert (m_cell_ptrs [target_cell_index] != 0); ColdProxy *proxy = new ColdProxy (target_cell_index, *this, info); diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 779a1ee2f..d9c78019e 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -593,6 +593,13 @@ public: */ void set_technology_name (const std::string &tech); + /** + * @brief Changes the technology name + * This method will only change the technology name, but does not re-assess the library links. + * It's provided mainly to support undo/redo and testing. + */ + void set_technology_name_without_update (const std::string &tech); + /** * @brief Accessor to the array repository */ @@ -1816,6 +1823,11 @@ public: */ const std::string &meta_info_value (const std::string &name) const; + /** + * @brief This event is triggered when the technology changes + */ + tl::Event technology_changed_event; + protected: /** * @brief Establish the graph's internals according to the dirty flags diff --git a/src/db/db/dbLibraryManager.cc b/src/db/db/dbLibraryManager.cc index 88e526c7c..2404f1217 100644 --- a/src/db/db/dbLibraryManager.cc +++ b/src/db/db/dbLibraryManager.cc @@ -69,19 +69,25 @@ LibraryManager::~LibraryManager () std::pair LibraryManager::lib_by_name (const std::string &name, const std::set &for_technologies) const { - iterator l = m_lib_by_name.find (name); - while (l != m_lib_by_name.end () && l->first == name) { - const db::Library *lptr = lib (l->second); - bool found = lptr->for_technologies (); - for (std::set::const_iterator t = for_technologies.begin (); t != for_technologies.end () && found; ++t) { - if (! lptr->is_for_technology (*t)) { - found = false; + iterator l; + + if (! for_technologies.empty ()) { + + l = m_lib_by_name.find (name); + while (l != m_lib_by_name.end () && l->first == name) { + const db::Library *lptr = lib (l->second); + bool found = lptr->for_technologies (); + for (std::set::const_iterator t = for_technologies.begin (); t != for_technologies.end () && found; ++t) { + if (! lptr->is_for_technology (*t)) { + found = false; + } } + if (found) { + return std::make_pair (true, l->second); + } + ++l; } - if (found) { - return std::make_pair (true, l->second); - } - ++l; + } // fallback: technology-unspecific libs diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index addce4eff..49a44f837 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -20,9 +20,12 @@ */ - - #include "dbLayout.h" +#include "dbLibraryManager.h" +#include "dbLibrary.h" +#include "dbColdProxy.h" +#include "dbLibraryProxy.h" +#include "dbTextWriter.h" #include "tlString.h" #include "tlUnitTest.h" @@ -464,3 +467,170 @@ TEST(4) prop_id = g.properties_repository ().properties_id (ps); EXPECT_EQ (el.property_ids_dirty, true); } + +static std::string l2s (const db::Layout &layout) +{ + tl::OutputStringStream os; + tl::OutputStream ostream (os); + db::TextWriter writer (ostream); + writer.write (layout); + return os.string (); +} + +TEST(5) +{ + // Technology management and library substitution + + db::cell_index_type ci; + unsigned int li; + db::Cell *cell; + + db::Library *lib_a = new db::Library (); + lib_a->set_name ("LIB"); + ci = lib_a->layout ().add_cell ("LIBCELL"); + li = lib_a->layout ().insert_layer (db::LayerProperties (1, 0)); + lib_a->layout ().cell (ci).shapes (li).insert (db::Box (0, 0, 100, 200)); + lib_a->add_technology ("A"); + db::LibraryManager::instance ().register_lib (lib_a); + + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "A").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "A").second, lib_a->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_ptr_by_name ("LIB", "A") == lib_a, true); + + db::Library *lib_b = new db::Library (); + lib_b->set_name ("LIB"); + ci = lib_b->layout ().add_cell ("LIBCELL"); + li = lib_b->layout ().insert_layer (db::LayerProperties (2, 0)); + lib_b->layout ().cell (ci).shapes (li).insert (db::Box (0, 0, 200, 100)); + lib_b->add_technology ("B"); + db::LibraryManager::instance ().register_lib (lib_b); + + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "B").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "B").second, lib_b->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_ptr_by_name ("LIB", "B") == lib_b, true); + + db::Library *lib_c = new db::Library (); + lib_c->set_name ("LIB"); + ci = lib_c->layout ().add_cell ("LIBCELL2"); + li = lib_c->layout ().insert_layer (db::LayerProperties (2, 0)); + lib_c->layout ().cell (ci).shapes (li).insert (db::Box (0, 0, 200, 100)); + lib_c->add_technology ("C"); + db::LibraryManager::instance ().register_lib (lib_c); + + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "C").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "C").second, lib_c->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_ptr_by_name ("LIB", "C") == lib_c, true); + + db::Manager m; + db::Layout l (&m); + EXPECT_EQ (l.technology_name (), ""); + + db::ProxyContextInfo info; + info.lib_name = "LIB"; + info.cell_name = "LIBCELL"; + + cell = l.recover_proxy (info); + EXPECT_EQ (dynamic_cast (cell) != 0, true); + EXPECT_EQ (cell->get_qualified_name (), "LIB.LIBCELL"); + EXPECT_EQ (cell->get_basic_name (), "LIBCELL"); + EXPECT_EQ (cell->get_display_name (), "LIB.LIBCELL"); + + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nend_cell\nend_lib\n"); + + // now restore the proxies + l.set_technology_name ("A"); + EXPECT_EQ (l.technology_name (), "A"); + + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 1 0 {0 0} {100 200}\nend_cell\nend_lib\n"); + + // now switch to cold proxies again as the technology does not have "LIBCELL" (but rather LIBCELL2) + l.set_technology_name ("C"); + EXPECT_EQ (l.technology_name (), "C"); + + cell = &l.cell (l.cell_by_name ("LIBCELL").second); + EXPECT_EQ (dynamic_cast (cell) != 0, true); + EXPECT_EQ (cell->get_qualified_name (), "LIB.LIBCELL"); + EXPECT_EQ (cell->get_basic_name (), "LIBCELL"); + EXPECT_EQ (cell->get_display_name (), "LIB.LIBCELL"); + + // NOTE: the box on 1/0 retained + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 1 0 {0 0} {100 200}\nend_cell\nend_lib\n"); + + // switch to another LIBCELL, this time using layer 2/0 + m.transaction ("switch_to_b"); + l.set_technology_name ("B"); + m.commit (); + + EXPECT_EQ (l.technology_name (), "B"); + cell = &l.cell (l.cell_by_name ("LIBCELL").second); + EXPECT_EQ (dynamic_cast (cell) != 0, true); + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 2 0 {0 0} {200 100}\nend_cell\nend_lib\n"); + + m.undo (); + EXPECT_EQ (l.technology_name (), "C"); + + cell = &l.cell (l.cell_by_name ("LIBCELL").second); + EXPECT_EQ (dynamic_cast (cell) != 0, true); + EXPECT_EQ (cell->get_qualified_name (), "LIB.LIBCELL"); + EXPECT_EQ (cell->get_basic_name (), "LIBCELL"); + EXPECT_EQ (cell->get_display_name (), "LIB.LIBCELL"); + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 1 0 {0 0} {100 200}\nend_cell\nend_lib\n"); + + m.redo (); + + EXPECT_EQ (l.technology_name (), "B"); + cell = &l.cell (l.cell_by_name ("LIBCELL").second); + EXPECT_EQ (dynamic_cast (cell) != 0, true); + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 2 0 {0 0} {200 100}\nend_cell\nend_lib\n"); + + db::LibraryManager::instance ().delete_lib (lib_a); + db::LibraryManager::instance ().delete_lib (lib_b); + db::LibraryManager::instance ().delete_lib (lib_c); +} + +TEST(6) +{ + // Cold proxies and context serialization + db::Cell *cell; + + db::Manager m; + db::Layout l (&m); + + EXPECT_EQ (l.technology_name (), ""); + + db::ProxyContextInfo info; + info.lib_name = "Basic"; + info.pcell_name = "CIRCLE"; + info.pcell_parameters ["actual_radius"] = tl::Variant (10.0); + info.pcell_parameters ["npoints"] = tl::Variant (8); + info.pcell_parameters ["layer"] = tl::Variant (db::LayerProperties (1, 0)); + + m.transaction ("import"); + cell = l.recover_proxy (info); + m.commit (); + EXPECT_EQ (cell->get_qualified_name (), "Basic.CIRCLE"); + EXPECT_EQ (cell->get_basic_name (), "CIRCLE"); + EXPECT_EQ (cell->get_display_name (), "Basic.CIRCLE(l=1/0,r=10,n=8)"); + + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-4142 -10000} {-10000 -4142} {-10000 4142} {-4142 10000} {4142 10000} {10000 4142} {10000 -4142} {4142 -10000} {-4142 -10000}\nend_cell\nend_lib\n"); + + db::ProxyContextInfo info2; + l.get_context_info (cell->cell_index (), info2); + info2.pcell_parameters ["actual_radius"] = tl::Variant (5.0); + + m.transaction ("modify"); + db::cell_index_type ci = cell->cell_index (); + l.recover_proxy_as (ci, info2); + m.commit (); + cell = &l.cell (ci); + EXPECT_EQ (cell->get_qualified_name (), "Basic.CIRCLE"); + EXPECT_EQ (cell->get_basic_name (), "CIRCLE"); + EXPECT_EQ (cell->get_display_name (), "Basic.CIRCLE(l=1/0,r=5,n=8)"); + + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-2071 -5000} {-5000 -2071} {-5000 2071} {-2071 5000} {2071 5000} {5000 2071} {5000 -2071} {2071 -5000} {-2071 -5000}\nend_cell\nend_lib\n"); + + m.undo (); + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-4142 -10000} {-10000 -4142} {-10000 4142} {-4142 10000} {4142 10000} {10000 4142} {10000 -4142} {4142 -10000} {-4142 -10000}\nend_cell\nend_lib\n"); + m.redo (); + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-2071 -5000} {-5000 -2071} {-5000 2071} {-2071 5000} {2071 5000} {5000 2071} {5000 -2071} {2071 -5000} {-2071 -5000}\nend_cell\nend_lib\n"); +} diff --git a/src/lay/lay/layTechnologyController.cc b/src/lay/lay/layTechnologyController.cc index d84876401..53f85f05a 100644 --- a/src/lay/lay/layTechnologyController.cc +++ b/src/lay/lay/layTechnologyController.cc @@ -285,15 +285,33 @@ bool TechnologyController::menu_activated (const std::string &symbol) const { if (symbol == "technology_selector:apply_technology") { + if (lay::LayoutView::current () && lay::LayoutView::current ()->active_cellview ().is_valid ()) { - // Cancels the current modes - changing the technology may make libraries unavailable - // for example. + if (mp_mw) { + + // Cancels the current modes - changing the technology may make libraries unavailable + // for example. mp_mw->cancel (); + + // apply technology with undo + mp_mw->manager ().transaction (tl::sprintf (tl::to_string (tr ("Apply technology '%s'")), m_current_technology)); + try { + lay::LayoutView::current ()->active_cellview ()->apply_technology (m_current_technology); + mp_mw->manager ().commit (); + } catch (...) { + mp_mw->manager ().cancel (); + throw; + } + + } else { + lay::LayoutView::current ()->active_cellview ()->apply_technology (m_current_technology); } - lay::LayoutView::current ()->active_cellview ()->apply_technology (m_current_technology); + } + return true; + } else { return lay::PluginDeclaration::menu_activated (symbol); } diff --git a/src/laybasic/laybasic/layCellView.cc b/src/laybasic/laybasic/layCellView.cc index e42317c43..1aacc1c67 100644 --- a/src/laybasic/laybasic/layCellView.cc +++ b/src/laybasic/laybasic/layCellView.cc @@ -59,6 +59,8 @@ LayoutHandle::LayoutHandle (db::Layout *layout, const std::string &filename) m_dirty (false), m_save_options_valid (false) { + layout->technology_changed_event.add (this, &LayoutHandle::on_technology_changed); + // layouts in the managed layouts space participate in spare proxy cleanup layout->do_cleanup (true); @@ -108,6 +110,12 @@ LayoutHandle::~LayoutHandle () file_watcher ().remove_file (filename ()); } +void +LayoutHandle::on_technology_changed () +{ + technology_changed_event (); +} + void LayoutHandle::layout_changed () { @@ -232,7 +240,6 @@ LayoutHandle::set_tech_name (const std::string &tn) { if (mp_layout && tn != tech_name ()) { mp_layout->set_technology_name (tn); - technology_changed_event (); } } diff --git a/src/laybasic/laybasic/layCellView.h b/src/laybasic/laybasic/layCellView.h index 5f036b728..7d2ac54e4 100644 --- a/src/laybasic/laybasic/layCellView.h +++ b/src/laybasic/laybasic/layCellView.h @@ -302,6 +302,8 @@ private: bool m_save_options_valid; db::LoadLayoutOptions m_load_options; + void on_technology_changed (); + static std::map ms_dict; static tl::FileSystemWatcher *mp_file_watcher; };