WIP: undo/redo for applying a technology.

This commit is contained in:
Matthias Koefferlein 2020-12-13 22:02:19 +01:00
parent 740f550964
commit 86e7fa56f0
8 changed files with 280 additions and 24 deletions

View File

@ -77,9 +77,9 @@ std::string
ColdProxy::get_basic_name () const
{
if (! mp_context_info->pcell_name.empty ()) {
return "<defunc>" + mp_context_info->pcell_name;
return "<defunct>" + mp_context_info->pcell_name;
} else if (! mp_context_info->cell_name.empty ()) {
return "<defunc>" + mp_context_info->cell_name;
return "<defunct>" + mp_context_info->cell_name;
} else {
return Cell::get_basic_name ();
}

View File

@ -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::vector<tl::Vari
// this variant must not exist yet for "get as" semantics
tl_assert (header->get_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);

View File

@ -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

View File

@ -69,19 +69,25 @@ LibraryManager::~LibraryManager ()
std::pair<bool, lib_id_type>
LibraryManager::lib_by_name (const std::string &name, const std::set<std::string> &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<std::string>::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<std::string>::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

View File

@ -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<db::ColdProxy *> (cell) != 0, true);
EXPECT_EQ (cell->get_qualified_name (), "<defunct>LIB.LIBCELL");
EXPECT_EQ (cell->get_basic_name (), "<defunct>LIBCELL");
EXPECT_EQ (cell->get_display_name (), "<defunct>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<db::ColdProxy *> (cell) != 0, true);
EXPECT_EQ (cell->get_qualified_name (), "<defunct>LIB.LIBCELL");
EXPECT_EQ (cell->get_basic_name (), "<defunct>LIBCELL");
EXPECT_EQ (cell->get_display_name (), "<defunct>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<db::LibraryProxy *> (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<db::ColdProxy *> (cell) != 0, true);
EXPECT_EQ (cell->get_qualified_name (), "<defunct>LIB.LIBCELL");
EXPECT_EQ (cell->get_basic_name (), "<defunct>LIBCELL");
EXPECT_EQ (cell->get_display_name (), "<defunct>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<db::LibraryProxy *> (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");
}

View File

@ -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);
}

View File

@ -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 ();
}
}

View File

@ -302,6 +302,8 @@ private:
bool m_save_options_valid;
db::LoadLayoutOptions m_load_options;
void on_technology_changed ();
static std::map <std::string, LayoutHandle *> ms_dict;
static tl::FileSystemWatcher *mp_file_watcher;
};