Merge remote-tracking branch 'origin/master' into wip

This commit is contained in:
Matthias Koefferlein 2026-07-20 23:20:47 +02:00
commit 5055d4fcf4
10 changed files with 412 additions and 34 deletions

View File

@ -707,14 +707,12 @@ Layout::set_technology_name (const std::string &tech)
for (db::Layout::iterator c = begin (); c != end (); ++c) { for (db::Layout::iterator c = begin (); c != end (); ++c) {
std::map<db::lib_id_type, db::lib_id_type>::const_iterator m;
db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (&*c); db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (&*c);
if (! lib_proxy) { if (! lib_proxy) {
continue; continue;
} }
if ((m = mapping.find (lib_proxy->lib_id ())) != mapping.end ()) { if (mapping.find (lib_proxy->lib_id ()) != mapping.end ()) {
db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ()); db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ());
db::Cell *lib_cell = &lib->layout ().cell (lib_proxy->library_cell_index ()); db::Cell *lib_cell = &lib->layout ().cell (lib_proxy->library_cell_index ());
@ -745,7 +743,10 @@ Layout::set_technology_name (const std::string &tech)
db::cell_index_type ci = lp->first->Cell::cell_index (); db::cell_index_type ci = lp->first->Cell::cell_index ();
db::PCellVariant *lib_pcell = lp->second; db::PCellVariant *lib_pcell = lp->second;
std::pair<bool, pcell_id_type> pn = lib_pcell->layout ()->pcell_by_name (lp->first->get_basic_name ().c_str ()); db::Library *new_lib = db::LibraryManager::instance ().lib (mapping [lp->first->lib_id ()]);
tl_assert (new_lib != 0);
std::pair<bool, pcell_id_type> pn = new_lib->layout ().pcell_by_name (lp->first->get_basic_name ().c_str ());
if (! pn.first) { if (! pn.first) {
@ -756,8 +757,6 @@ Layout::set_technology_name (const std::string &tech)
} else { } else {
db::Library *new_lib = db::LibraryManager::instance ().lib (mapping [lp->first->lib_id ()]);
const db::PCellDeclaration *old_pcell_decl = lib_pcell->layout ()->pcell_declaration (lib_pcell->pcell_id ()); const db::PCellDeclaration *old_pcell_decl = lib_pcell->layout ()->pcell_declaration (lib_pcell->pcell_id ());
const db::PCellDeclaration *new_pcell_decl = new_lib->layout ().pcell_declaration (pn.second); const db::PCellDeclaration *new_pcell_decl = new_lib->layout ().pcell_declaration (pn.second);
if (! old_pcell_decl || ! new_pcell_decl) { if (! old_pcell_decl || ! new_pcell_decl) {

View File

@ -27,6 +27,7 @@
#include "layStream.h" #include "layStream.h"
#include "layAbstractMenu.h" #include "layAbstractMenu.h"
#include "layMainWindow.h" #include "layMainWindow.h"
#include "tlLog.h"
#include "ui_MainConfigPage.h" #include "ui_MainConfigPage.h"
#include "ui_MainConfigPage2.h" #include "ui_MainConfigPage2.h"
#include "ui_MainConfigPage3.h" #include "ui_MainConfigPage3.h"
@ -412,15 +413,31 @@ CustomizeMenuConfigPage::apply (const std::vector<std::pair<std::string, std::st
// gets the current bindings and merges with the given ones // gets the current bindings and merges with the given ones
m_current_bindings = mp_dispatcher->menu ()->get_shortcuts (false); m_current_bindings = mp_dispatcher->menu ()->get_shortcuts (false);
// retained bindings, even if not configured
std::vector<std::map<std::string, std::string>::iterator> retained;
std::set<std::string> shortcuts;
// initialize configured bindings, clear others (for now) and remember
std::map<std::string, std::string> b; std::map<std::string, std::string> b;
b.insert (key_bindings.begin (), key_bindings.end ()); b.insert (key_bindings.begin (), key_bindings.end ());
for (std::map<std::string, std::string>::iterator kb = m_current_bindings.begin (); kb != m_current_bindings.end (); ++kb) { for (auto kb = m_current_bindings.begin (); kb != m_current_bindings.end (); ++kb) {
std::map<std::string, std::string>::iterator bb = b.find (kb->first); std::map<std::string, std::string>::iterator bb = b.find (kb->first);
if (bb != b.end ()) { if (bb != b.end ()) {
lay::Action *a = mp_dispatcher->menu ()->action (kb->first); lay::Action *a = mp_dispatcher->menu ()->action (kb->first);
kb->second = a->get_effective_shortcut_for (bb->second); kb->second = a->get_effective_shortcut_for (bb->second);
} else { if (! kb->second.empty ()) {
kb->second.clear (); shortcuts.insert (kb->second);
}
} else if (! kb->second.empty ()) {
retained.push_back (kb);
}
}
// retain key bindings which don't conflict
for (auto i = retained.begin (); i != retained.end (); ++i) {
if (shortcuts.find ((*i)->second) != shortcuts.end ()) {
tl::warn << tl::sprintf (tl::to_string (tr ("Resetting key binding for '%s' (was '%s') because of conflicts")), (*i)->first, (*i)->second);
(*i)->second.clear ();
} }
} }

View File

@ -1970,6 +1970,18 @@ MainWindow::redraw ()
} }
} }
void
MainWindow::cm_grid_decrease ()
{
change_grid (-1);
}
void
MainWindow::cm_grid_increase ()
{
change_grid (1);
}
void void
MainWindow::cm_cancel () MainWindow::cm_cancel ()
{ {
@ -4041,6 +4053,10 @@ MainWindow::menu_activated (const std::string &symbol)
cm_bookmark_view (); cm_bookmark_view ();
} else if (symbol == "cm_cancel") { } else if (symbol == "cm_cancel") {
cm_cancel (); cm_cancel ();
} else if (symbol == "cm_grid_decrease") {
cm_grid_decrease ();
} else if (symbol == "cm_grid_increase") {
cm_grid_increase ();
} else if (symbol == "cm_save_layer_props") { } else if (symbol == "cm_save_layer_props") {
cm_save_layer_props (); cm_save_layer_props ();
} else if (symbol == "cm_load_layer_props") { } else if (symbol == "cm_load_layer_props") {
@ -4137,38 +4153,114 @@ MainWindow::menu_changed ()
dm_do_update_menu (); dm_do_update_menu ();
} }
void std::vector<double>
MainWindow::do_update_grids () MainWindow::default_grids () const
{ {
const std::vector<double> *grids = &m_default_grids;
double default_grid = m_default_grid;
std::vector<double> tech_grids;
lay::TechnologyController *tc = lay::TechnologyController::instance (); lay::TechnologyController *tc = lay::TechnologyController::instance ();
if (tc && tc->active_technology ()) { if (tc && tc->active_technology ()) {
tech_grids = tc->active_technology ()->default_grid_list (); std::vector<double> tech_grids = tc->active_technology ()->default_grid_list ();
if (! tech_grids.empty ()) { if (! tech_grids.empty ()) {
grids = &tech_grids; return tech_grids;
default_grid = tc->active_technology ()->default_grid ();
} }
} }
if (default_grid > db::epsilon) { return m_default_grids;
for (auto g = grids->begin (); g != grids->end (); ++g) { }
double
MainWindow::default_grid () const
{
lay::TechnologyController *tc = lay::TechnologyController::instance ();
if (tc && tc->active_technology ()) {
auto tech_grids = tc->active_technology ()->default_grid_list ();
if (! tech_grids.empty ()) {
return tc->active_technology ()->default_grid ();
}
}
return m_default_grid;
}
void
MainWindow::change_grid (int dir)
{
std::vector<double> grids = default_grids ();
if (grids.empty ()) {
return;
}
std::sort (grids.begin (), grids.end ());
size_t i = 0;
for (std::vector<double>::const_iterator g = grids.begin (); g != grids.end (); ++g, ++i) {
if (db::coord_traits<db::DCoord>::equals (*g, m_grid_micron)) {
break;
}
}
if (i == grids.size ()) {
i = (dir > 0 ? grids.size () - 1 : 0);
} else {
if (dir > 0) {
if (i + 1 < grids.size ()) {
++i;
}
} else if (dir < 0) {
if (i > 0) {
--i;
}
}
}
dispatcher ()->config_set (cfg_grid, grids [i]);
}
void
MainWindow::do_update_grids ()
{
std::vector<double> grids = default_grids ();
double def_grid = default_grid ();
if (def_grid > db::epsilon) {
for (auto g = grids.begin (); g != grids.end (); ++g) {
if (db::coord_traits<db::DCoord>::equals (*g, m_grid_micron)) { if (db::coord_traits<db::DCoord>::equals (*g, m_grid_micron)) {
default_grid = 0.0; def_grid = 0.0;
break; break;
} }
} }
} }
if (default_grid > db::epsilon) { if (def_grid > db::epsilon) {
dispatcher ()->config_set (cfg_grid, default_grid); dispatcher ()->config_set (cfg_grid, def_grid);
} }
do_update_menu (); do_update_menu ();
} }
namespace {
class GenericMenuAction
: public Action
{
public:
GenericMenuAction (lay::Dispatcher *dispatcher, const std::string &title, const std::string &symbol)
: Action (title), mp_dispatcher (dispatcher), m_symbol (symbol)
{ }
void triggered ()
{
if (mp_dispatcher) {
mp_dispatcher->menu_activated (m_symbol);
}
}
private:
Dispatcher *mp_dispatcher;
std::string m_symbol;
};
}
void void
MainWindow::do_update_menu () MainWindow::do_update_menu ()
{ {
@ -4176,27 +4268,26 @@ MainWindow::do_update_menu ()
m_default_grids_updated = false; m_default_grids_updated = false;
const std::vector<double> *grids = &m_default_grids; std::vector<double> grids = default_grids ();
std::vector<double> tech_grids; double def_grid = default_grid ();
lay::TechnologyController *tc = lay::TechnologyController::instance ();
if (tc && tc->active_technology ()) {
tech_grids = tc->active_technology ()->default_grid_list ();
if (! tech_grids.empty ()) {
grids = &tech_grids;
}
}
std::vector<std::string> group = menu ()->group ("default_grids_group"); std::vector<std::string> group = menu ()->group ("default_grids_group");
for (std::vector<std::string>::const_iterator t = group.begin (); t != group.end (); ++t) { for (std::vector<std::string>::const_iterator t = group.begin (); t != group.end (); ++t) {
std::vector<std::string> items = menu ()->items (*t); std::vector<std::string> items = menu ()->items (*t);
for (std::vector<std::string>::const_iterator i = items.begin (); i != items.end (); ++i) { for (std::vector<std::string>::const_iterator i = items.begin (); i != items.end (); ++i) {
menu ()->delete_item (*i); menu ()->delete_item (*i);
} }
menu ()->insert_item (*t + ".end", "finer_grid", new GenericMenuAction (dispatcher (), tl::to_string (tr ("Finer Grid(G)")), "cm_grid_decrease"));
menu ()->insert_item (*t + ".end", "coarser_grid", new GenericMenuAction (dispatcher (), tl::to_string (tr ("Coarser Grid(Shift+G)")), "cm_grid_increase"));
menu ()->insert_separator (*t + ".end", "default_grids_group_separator");
} }
int i = 1; int i = 1;
for (std::vector<double>::const_iterator g = grids->begin (); g != grids->end (); ++g, ++i) { for (std::vector<double>::const_iterator g = grids.begin (); g != grids.end (); ++g, ++i) {
std::string name = "default_grid_" + tl::to_string (i); std::string name = "default_grid_" + tl::to_string (i);
@ -4208,6 +4299,10 @@ MainWindow::do_update_menu ()
gs = tl::to_string (*g) + tl::to_string (QObject::tr (" um")); gs = tl::to_string (*g) + tl::to_string (QObject::tr (" um"));
} }
if (def_grid > 0 && db::coord_traits<db::DCoord>::equals (*g, def_grid)) {
gs += tl::to_string (tr (" \\(default)"));
}
lay::Action *ga = new lay::ConfigureAction (gs, cfg_grid, tl::to_string (*g)); lay::Action *ga = new lay::ConfigureAction (gs, cfg_grid, tl::to_string (*g));
ga->set_checkable (true); ga->set_checkable (true);
ga->set_checked (db::coord_traits<db::DCoord>::equals (*g, m_grid_micron)); ga->set_checked (db::coord_traits<db::DCoord>::equals (*g, m_grid_micron));

View File

@ -277,6 +277,28 @@ public:
*/ */
double grid_micron () const; double grid_micron () const;
/**
* @brief Gets a list of the default grids
*
* The default grids are globally defined, but may change depending on the technology
* selected in the current view.
*/
std::vector<double> default_grids () const;
/**
* @brief Gets the default grid
*
* The default (fallback) grid is the one marked with "!" in the grid list
* and it is used, when the current grid is not one of the provided grids
* in the default grid list.
*
* The default grid is globally defined, but my change depending on the technology
* selected in the current view.
*
* If no such grid exists, 0 is returned.
*/
double default_grid () const;
/** /**
* @brief Hierarchy level selection setter * @brief Hierarchy level selection setter
*/ */
@ -818,6 +840,8 @@ private:
void cm_manage_bookmarks (); void cm_manage_bookmarks ();
void cm_bookmark_view (); void cm_bookmark_view ();
void cm_cancel (); void cm_cancel ();
void cm_grid_decrease ();
void cm_grid_increase ();
void cm_save_layer_props (); void cm_save_layer_props ();
void cm_load_layer_props (); void cm_load_layer_props ();
void cm_save_session (); void cm_save_session ();
@ -863,6 +887,8 @@ private:
void update_tab_title (int i); void update_tab_title (int i);
void add_view (LayoutViewWidget *view); void add_view (LayoutViewWidget *view);
void change_grid (int dir);
bool can_close (); bool can_close ();
lay::CellViewRef create_or_load_layout (const std::string *filename, const db::LoadLayoutOptions *options, const std::string &tech, const int mode); lay::CellViewRef create_or_load_layout (const std::string *filename, const db::LoadLayoutOptions *options, const std::string &tech, const int mode);
int do_create_view (); int do_create_view ();

View File

@ -29,6 +29,7 @@
#include "layPixelBufferPainter.h" #include "layPixelBufferPainter.h"
#include "laySnap.h" #include "laySnap.h"
#include "tlColor.h" #include "tlColor.h"
#include "tlInternational.h"
#include "dbTrans.h" #include "dbTrans.h"
#if defined(HAVE_QT) #if defined(HAVE_QT)
@ -118,7 +119,7 @@ GridNetPluginDeclaration::get_options (std::vector < std::pair<std::string, std:
lay::ConfigPage * lay::ConfigPage *
GridNetPluginDeclaration::config_page (QWidget *parent, std::string &title) const GridNetPluginDeclaration::config_page (QWidget *parent, std::string &title) const
{ {
title = tl::to_string (QObject::tr ("Display|Background")); title = tl::to_string (tr ("Display|Background"));
return new GridNetConfigPage (parent); return new GridNetConfigPage (parent);
} }
#endif #endif
@ -216,9 +217,21 @@ GridNet::configure (const std::string &name, const std::string &value)
double g = 0; double g = 0;
tl::from_string (value, g); tl::from_string (value, g);
if (fabs (g - m_grid) > 1e-6) { if (fabs (g - m_grid) > 1e-6) {
m_grid = g; m_grid = g;
need_update = true; need_update = true;
std::string gs;
if (m_grid < 0.4) {
// pick nm units below 400nm
gs = tl::to_string (m_grid * 1000.0) + tl::to_string (tr (" nm"));
} else {
gs = tl::to_string (m_grid) + tl::to_string (tr (" um"));
}
mp_view->message (tl::sprintf (tl::to_string (tr ("Grid: %s")), gs));
} }
taken = false; // to let others use the grid too. taken = false; // to let others use the grid too.
} else { } else {

View File

@ -43,6 +43,11 @@ namespace pya
void void
pya_object_deallocate (PyObject *self) pya_object_deallocate (PyObject *self)
{ {
// Clear weak refs - needed for signal binding and other purposes
#if PY_VERSION_HEX > 0x03020000
PyObject_ClearWeakRefs (self);
#endif
// This avoids an assertion in debug builds (Python, gcmodule.c - update_refs). // This avoids an assertion in debug builds (Python, gcmodule.c - update_refs).
// In short, the GC expects not to see objects with refcount 0 and asserts. // In short, the GC expects not to see objects with refcount 0 and asserts.
// However, due to triggering of signals or similar, the destructor call below // However, due to triggering of signals or similar, the destructor call below

View File

@ -337,6 +337,11 @@ public:
#if PY_VERSION_HEX >= 0x030D0000 #if PY_VERSION_HEX >= 0x030D0000
// crashes with this option set // crashes with this option set
type->tp_flags &= ~Py_TPFLAGS_INLINE_VALUES; type->tp_flags &= ~Py_TPFLAGS_INLINE_VALUES;
#endif
#if PY_VERSION_HEX < 0x03000000
type->tp_flags |= Py_TPFLAGS_HAVE_WEAKREFS;
type->tp_weaklistoffset = type->tp_basicsize;
type->tp_basicsize += sizeof (PyObject *);
#endif #endif
type->tp_basicsize += sizeof (PYAObjectBase); type->tp_basicsize += sizeof (PYAObjectBase);
type->tp_init = &pya_object_init; type->tp_init = &pya_object_init;

View File

@ -129,6 +129,7 @@ RUBYTEST (dbNetlistReaderTests, "dbNetlistReaderTests.rb")
RUBYTEST (dbNetlistCompare, "dbNetlistCompare.rb") RUBYTEST (dbNetlistCompare, "dbNetlistCompare.rb")
RUBYTEST (dbPathTest, "dbPathTest.rb") RUBYTEST (dbPathTest, "dbPathTest.rb")
RUBYTEST (dbPCells, "dbPCells.rb") RUBYTEST (dbPCells, "dbPCells.rb")
RUBYTEST (dbPCellsRebind, "dbPCellsRebind.rb")
RUBYTEST (dbPointTest, "dbPointTest.rb") RUBYTEST (dbPointTest, "dbPointTest.rb")
RUBYTEST (dbPolygonTest, "dbPolygonTest.rb") RUBYTEST (dbPolygonTest, "dbPolygonTest.rb")
RUBYTEST (dbRegionTest, "dbRegionTest.rb") RUBYTEST (dbRegionTest, "dbRegionTest.rb")

View File

@ -22,6 +22,7 @@ import os
import sys import sys
import gc import gc
import copy import copy
import weakref
# Set this to True to disable some tests involving exceptions # Set this to True to disable some tests involving exceptions
leak_check = "TEST_LEAK_CHECK" in os.environ leak_check = "TEST_LEAK_CHECK" in os.environ
@ -3352,6 +3353,20 @@ class BasicTest(unittest.TestCase):
self.assertEqual(bc.str(), "xyz") self.assertEqual(bc.str(), "xyz")
self.assertEqual(bnc.str(), "xyz") self.assertEqual(bnc.str(), "xyz")
# weak refs
def test_94(self):
b = pya.B()
b.set_str("abc")
r = weakref.ref(b)
self.assertEqual(r() is None, False)
self.assertEqual(r().str(), "abc")
b = None
self.assertEqual(r() is None, True)
# run unit tests # run unit tests
if __name__ == '__main__': if __name__ == '__main__':
suite = unittest.TestSuite() suite = unittest.TestSuite()

202
testdata/ruby/dbPCellsRebind.rb vendored Normal file
View File

@ -0,0 +1,202 @@
# encoding: UTF-8
# KLayout Layout Viewer
# Copyright (C) 2006-2026 Matthias Koefferlein
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
if !$:.member?(File::dirname($0))
$:.push(File::dirname($0))
end
load("test_prologue.rb")
class PCellRebindVar < RBA::PCellDeclaration
def initialize(name, layer, w_name, w_default)
@name = name
@layer = layer
@w_name = w_name
@w_default = w_default
end
def display_text(parameters)
return "#{@name}(L=#{@layer},#{@w_name}=#{parameters[0].to_s},H=#{parameters[1].to_s})"
end
def get_parameters
# prepare a set of parameter declarations
param = []
param.push(RBA::PCellParameterDeclaration.new(@w_name, RBA::PCellParameterDeclaration::TypeDouble, "Width", @w_default))
param.push(RBA::PCellParameterDeclaration.new("H", RBA::PCellParameterDeclaration::TypeDouble, "Height", 1.0))
return param
end
def get_layers(parameters)
return [ RBA::LayerInfo::new(@layer, 0) ]
end
def produce(layout, layers, parameters, cell)
# fetch the parameters
w = parameters[0]
h = parameters[1]
# create the shape
cell.shapes(layout.layer(@layer, 0)).insert(RBA::DBox.new(-w / 2, -h / 2, w / 2, h / 2))
end
end
class RebindTestLib < RBA::Library
def initialize(var)
# set the description
self.description = "PCell rebind test lib"
# create the PCell declarations
if var == 0
layout.register_pcell("Box0", PCellRebindVar::new("Box0", 0, "W", 3.0))
layout.register_pcell("Box1", PCellRebindVar::new("Box1", 1, "W", 2.0))
layout.register_pcell("Box2A", PCellRebindVar::new("Box2A", 2, "W", 1.0))
else
layout.register_pcell("Box2B", PCellRebindVar::new("Box2B", 12, "W", 1.5))
layout.register_pcell("Box1", PCellRebindVar::new("Box1", 11, "WW", 2.5))
layout.register_pcell("Box0", PCellRebindVar::new("Box0", 10, "W", 3.5))
end
self.technology = (var == 0 ? "T0" : "T1")
register("RebindTestLib")
end
end
def c2s(cell)
ly = cell.layout
s = []
ly.layer_indexes.collect { |li| ly.get_info(li) }.sort { |a,b| [ a.layer, a.datatype ] <=> [ b.layer, b.datatype ] }.each do |lp|
bbox = cell.dbbox(ly.layer(lp))
bbox.empty? || (s << lp.to_s + ":" + bbox.to_s)
end
s.join(";")
end
class DBPCellRebind_TestClass < TestBase
def test_1
tl0 = RebindTestLib::new(0)
tl1 = RebindTestLib::new(1)
assert_equal(tl0.is_for_technology("T0"), true)
assert_equal(tl0.is_for_technology("T1"), false)
assert_equal(tl1.is_for_technology("T0"), false)
assert_equal(tl1.is_for_technology("T1"), true)
ly = RBA::Layout::new
ly.technology_name = "T0"
top = ly.create_cell("TOP")
# in T0:
# Box0 -> on layer 0, w parameter is "W" with default 3.0
# Box1 -> on layer 1, w parameter is "W" with default 2.0
# Box2A -> on layer 2, w parameter is "W" with default 1.0
# Box2B does not exist
c0 = ly.create_cell("Box0", "RebindTestLib", { "W" => 0.3, "H" => 0.5 })
c1 = ly.create_cell("Box1", "RebindTestLib", { "W" => 0.4, "H" => 0.6 })
c2 = ly.create_cell("Box2A", "RebindTestLib", { "W" => 0.5, "H" => 0.7 })
top.insert(RBA::CellInstArray::new(c0.cell_index(), RBA::Trans::new))
top.insert(RBA::CellInstArray::new(c1.cell_index(), RBA::Trans::new))
top.insert(RBA::CellInstArray::new(c2.cell_index(), RBA::Trans::new))
assert_equal(c0.display_title, "RebindTestLib.Box0(L=0,W=0.3,H=0.5)")
assert_equal(c2s(c0), "0/0:(-0.15,-0.25;0.15,0.25)")
assert_equal(c1.display_title, "RebindTestLib.Box1(L=1,W=0.4,H=0.6)")
assert_equal(c2s(c1), "1/0:(-0.2,-0.3;0.2,0.3)")
assert_equal(c2.display_title, "RebindTestLib.Box2A(L=2,W=0.5,H=0.7)")
assert_equal(c2s(c2), "2/0:(-0.25,-0.35;0.25,0.35)")
# in T0:
# Box0 -> on layer 0, w parameter is "W" with default 3.5
# Box1 -> on layer 1, w parameter is "WW" with default 2.5
# Box2A does not exist
# Box2B -> on layer 2, w parameter is "W" with default 1.5
ly.technology_name = "T1"
assert_equal(c0.destroyed, false)
assert_equal(c1.destroyed, false)
assert_equal(c2.destroyed, true) # becomes a cold proxy
c0 = ly.cell("Box0")
c1 = ly.cell("Box1")
c2 = ly.cell("Box2A")
# layer changed, but parameters can be translated
assert_equal(c0.display_title, "RebindTestLib.Box0(L=10,W=0.3,H=0.5)")
assert_equal(c2s(c0), "10/0:(-0.15,-0.25;0.15,0.25)")
# Layer changed and "W" parameter can't be translated as the name changed to "WW"
assert_equal(c1.display_title, "RebindTestLib.Box1(L=11,WW=2.5,H=0.6)")
assert_equal(c2s(c1), "11/0:(-1.25,-0.3;1.25,0.3)")
# Box2A does no longer exist -> cold proxy
assert_equal(c2.display_title, "<defunct>RebindTestLib.Box2A")
assert_equal(c2s(c2), "2/0:(-0.25,-0.35;0.25,0.35)")
ly.technology_name = "T0"
assert_equal(c0.destroyed, false)
assert_equal(c1.destroyed, false)
assert_equal(c2.destroyed, true) # became an active PCell variant again
c0 = ly.cell("Box0")
c1 = ly.cell("Box1")
c2 = ly.cell("Box2A")
# layer changed, but parameters can be translated
assert_equal(c0.display_title, "RebindTestLib.Box0(L=0,W=0.3,H=0.5)")
assert_equal(c2s(c0), "0/0:(-0.15,-0.25;0.15,0.25)")
# Layer changed and "WW" parameter can't be translated as the name changed back to "W"
assert_equal(c1.display_title, "RebindTestLib.Box1(L=1,W=2.0,H=0.6)")
assert_equal(c2s(c1), "1/0:(-1,-0.3;1,0.3)")
# Box2A is back to normal as the cold proxy kept the information
assert_equal(c2.display_title, "RebindTestLib.Box2A(L=2,W=0.5,H=0.7)")
assert_equal(c2s(c2), "2/0:(-0.25,-0.35;0.25,0.35)")
tl0._destroy
tl1._destroy
end
end
load("test_epilogue.rb")