Merge pull request #2308 from KLayout/bugfix/issue-2305

Bugfix/issue 2305
This commit is contained in:
Matthias Köfferlein 2026-04-14 08:57:19 +02:00 committed by GitHub
commit 72c4af9f94
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
67 changed files with 2632 additions and 779 deletions

View File

@ -34,6 +34,7 @@ SOURCES = \
dbEdgeProcessor.cc \ dbEdgeProcessor.cc \
dbEdges.cc \ dbEdges.cc \
dbEdgesLocalOperations.cc \ dbEdgesLocalOperations.cc \
dbFileBasedLibrary.cc \
dbFillTool.cc \ dbFillTool.cc \
dbFuzzyCellMapping.cc \ dbFuzzyCellMapping.cc \
dbGenericShapeIterator.cc \ dbGenericShapeIterator.cc \
@ -275,6 +276,7 @@ HEADERS = \
dbEdges.h \ dbEdges.h \
dbEdgesLocalOperations.h \ dbEdgesLocalOperations.h \
dbEdgesToContours.h \ dbEdgesToContours.h \
dbFileBasedLibrary.h \
dbFillTool.h \ dbFillTool.h \
dbFuzzyCellMapping.h \ dbFuzzyCellMapping.h \
dbGenericShapeIterator.h \ dbGenericShapeIterator.h \

View File

@ -857,7 +857,7 @@ public:
void check_locked () const; void check_locked () const;
/** /**
* @brief Tell, if this cell is a proxy cell * @brief Gets a value indicating if this cell is a proxy cell
* *
* Proxy cells are such whose layout represents a snapshot of another entity. * Proxy cells are such whose layout represents a snapshot of another entity.
* Such cells can be PCell variants or library references for example. * Such cells can be PCell variants or library references for example.
@ -867,6 +867,17 @@ public:
return false; return false;
} }
/**
* @brief Gets a value indicating that this cell is a replica that can be skipped
*
* This attribute is evaluated by file writers to skip cell replicas for
* library cells that do not want to replicated.
*/
virtual bool can_skip_replica () const
{
return false;
}
/** /**
* @brief Sets the cell name * @brief Sets the cell name
* *

View File

@ -60,14 +60,36 @@ ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOr
} }
i->second->push_back (this); i->second->push_back (this);
} }
layout.register_cold_proxy (this);
} }
ColdProxy::~ColdProxy () ColdProxy::~ColdProxy ()
{ {
if (layout ()) {
layout ()->unregister_cold_proxy (this);
}
delete mp_context_info; delete mp_context_info;
mp_context_info = 0; mp_context_info = 0;
} }
void
ColdProxy::unregister ()
{
if (layout ()) {
layout ()->unregister_cold_proxy (this);
}
}
void
ColdProxy::reregister ()
{
if (layout ()) {
layout ()->register_cold_proxy (this);
}
}
Cell * Cell *
ColdProxy::clone (Layout &layout) const ColdProxy::clone (Layout &layout) const
{ {

View File

@ -81,6 +81,16 @@ public:
return true; return true;
} }
/**
* @brief Reimplemented from Cell: unregisters the proxy at the layout
*/
virtual void unregister ();
/**
* @brief Reimplemented from Cell: reregisters the proxy at the layout
*/
virtual void reregister ();
/** /**
* @brief Gets a list of cold proxies for a given library name * @brief Gets a list of cold proxies for a given library name
*/ */

View File

@ -692,6 +692,11 @@ public:
return false; return false;
} }
virtual bool supports_context () const
{
return false;
}
virtual tl::XMLElementBase *xml_reader_options_element () const virtual tl::XMLElementBase *xml_reader_options_element () const
{ {
return new db::ReaderOptionsXMLElement<db::CommonReaderOptions> ("common", return new db::ReaderOptionsXMLElement<db::CommonReaderOptions> ("common",

View File

@ -0,0 +1,146 @@
/*
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
*/
#include "dbFileBasedLibrary.h"
#include "dbReader.h"
#include "dbCellMapping.h"
#include "tlFileUtils.h"
#include "tlStream.h"
namespace db
{
// -------------------------------------------------------------------------------------------
FileBasedLibrary::FileBasedLibrary (const std::string &path, const std::string &name)
: 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
FileBasedLibrary::merge_with_other_layout (const std::string &path)
{
m_other_paths.push_back (path);
if (m_is_loaded) {
merge_impl (path);
}
}
bool
FileBasedLibrary::is_for_path (const std::string &path)
{
return m_other_paths.empty () && m_path == path;
}
bool
FileBasedLibrary::is_for_paths (const std::vector<std::string> &paths)
{
if (paths.size () != m_other_paths.size () + 1) {
return false;
}
// this check is purely based on path strings
std::set<std::string> p1 (paths.begin (), paths.end ());
std::set<std::string> p2;
p2.insert (m_path);
p2.insert (m_other_paths.begin (), m_other_paths.end ());
return p1 == p2;
}
std::string
FileBasedLibrary::load ()
{
if (! m_is_loaded) {
return reload ();
} else {
return get_name ();
}
}
std::string
FileBasedLibrary::reload ()
{
std::string name = m_name.empty () ? tl::basename (m_path) : m_name;
layout ().clear ();
tl::InputStream stream (m_path);
db::Reader reader (stream);
reader.read (layout ());
// Use the libname if there is one
if (m_name.empty ()) {
db::Layout::meta_info_name_id_type libname_name_id = layout ().meta_info_name_id ("libname");
for (db::Layout::meta_info_iterator m = layout ().begin_meta (); m != layout ().end_meta (); ++m) {
if (m->first == libname_name_id && ! m->second.value.is_nil ()) {
name = m->second.value.to_string ();
break;
}
}
}
for (auto p = m_other_paths.begin (); p != m_other_paths.end (); ++p) {
merge_impl (*p);
}
m_is_loaded = true;
return name;
}
void
FileBasedLibrary::merge_impl (const std::string &path)
{
db::Layout ly;
tl::InputStream stream (path);
db::Reader reader (stream);
reader.read (ly);
std::vector<db::cell_index_type> target_cells, source_cells;
// collect the cells to pull in (all top cells of the library layout)
// NOTE: cells are not overwritten - the first layout wins, in terms
// of cell names and also in terms of database unit.
for (auto c = ly.begin_top_down (); c != ly.end_top_cells (); ++c) {
std::string cn = ly.cell_name (*c);
if (! layout ().has_cell (cn.c_str ())) {
source_cells.push_back (*c);
target_cells.push_back (layout ().add_cell (cn.c_str ()));
}
}
db::CellMapping cm;
cm.create_multi_mapping_full (layout (), target_cells, ly, source_cells);
layout ().copy_tree_shapes (ly, cm);
}
}

View File

@ -0,0 +1,115 @@
/*
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
*/
#ifndef HDR_dbFileBasedLibrary
#define HDR_dbFileBasedLibrary
#include "dbLibrary.h"
#include "dbCommon.h"
#include <string>
#include <list>
namespace db
{
/**
* @brief A Library specialization that ties a library to a file
*
* This object supports loading a library from multiple files and merging them into
* a single library (e.g. for loading a directory of cell files).
*/
class DB_PUBLIC FileBasedLibrary
: public db::Library
{
public:
/**
* @brief Creates a file-based library object
*
* @param path The file path
* @param name The library name
*
* If the library name is an empty string, the library name is taken from
* a GDS LIBNAME or from the file name in the path.
*
* Note that you need to call "load" in order to actually load the file.
*/
FileBasedLibrary (const std::string &path, const std::string &name = std::string ());
/**
* @brief Merges another file into this library
*
* If the library was not loaded already, the merge requests are postponed
* until "load" is called.
*/
void merge_with_other_layout (const std::string &path);
/**
* @brief Loads the files
*
* If the files are already loaded, this method does nothing.
* It returns the name of the library derived from the first file
* or the name given in the constructor. The constructor name has
* priority.
*/
std::string load ();
/**
* @brief Implements the reload feature
*/
virtual std::string reload ();
/**
* @brief Set the paths
* This method is provided for test purposes only.
*/
void set_paths (const std::string &path, const std::list<std::string> &other_paths = std::list<std::string> ())
{
m_path = path;
m_other_paths = other_paths;
}
/**
* @brief Gets a value indicating whether the library is for the given path
*/
bool is_for_path (const std::string &path);
/**
* @brief Gets a value indicating whether the library is for the given path
*/
bool is_for_paths (const std::vector<std::string> &paths);
private:
std::string m_name;
std::string m_path;
std::list<std::string> m_other_paths;
bool m_is_loaded;
void merge_impl (const std::string &path);
};
}
#endif

View File

@ -337,6 +337,37 @@ private:
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// Implementation of the ProxyContextInfo class // Implementation of the ProxyContextInfo class
bool
LayoutOrCellContextInfo::operator== (const LayoutOrCellContextInfo &other) const
{
return lib_name == other.lib_name &&
cell_name == other.cell_name &&
pcell_name == other.pcell_name &&
pcell_parameters == other.pcell_parameters &&
meta_info == other.meta_info;
}
bool
LayoutOrCellContextInfo::operator< (const LayoutOrCellContextInfo &other) const
{
if (lib_name != other.lib_name) {
return lib_name < other.lib_name;
}
if (cell_name != other.cell_name) {
return cell_name < other.cell_name;
}
if (pcell_name != other.pcell_name) {
return pcell_name < other.pcell_name;
}
if (pcell_parameters != other.pcell_parameters) {
return pcell_parameters < other.pcell_parameters;
}
if (meta_info != other.meta_info) {
return meta_info < other.meta_info;
}
return false;
}
LayoutOrCellContextInfo LayoutOrCellContextInfo
LayoutOrCellContextInfo::deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to) LayoutOrCellContextInfo::deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to)
{ {
@ -540,6 +571,7 @@ Layout::clear ()
m_pcell_ids.clear (); m_pcell_ids.clear ();
m_lib_proxy_map.clear (); m_lib_proxy_map.clear ();
m_cold_proxy_map.clear ();
m_meta_info.clear (); m_meta_info.clear ();
} }
@ -568,6 +600,7 @@ Layout::operator= (const Layout &d)
} }
m_lib_proxy_map = d.m_lib_proxy_map; m_lib_proxy_map = d.m_lib_proxy_map;
m_cold_proxy_map = d.m_cold_proxy_map;
m_cell_ptrs.resize (d.m_cell_ptrs.size (), 0); m_cell_ptrs.resize (d.m_cell_ptrs.size (), 0);
@ -809,6 +842,7 @@ Layout::mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat
db::mem_stat (stat, purpose, cat, m_pcells, true, (void *) this); db::mem_stat (stat, purpose, cat, m_pcells, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_pcell_ids, true, (void *) this); db::mem_stat (stat, purpose, cat, m_pcell_ids, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_lib_proxy_map, true, (void *) this); db::mem_stat (stat, purpose, cat, m_lib_proxy_map, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_cold_proxy_map, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_meta_info, true, (void *) this); db::mem_stat (stat, purpose, cat, m_meta_info, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_shape_repository, true, (void *) this); db::mem_stat (stat, purpose, cat, m_shape_repository, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_array_repository, true, (void *) this); db::mem_stat (stat, purpose, cat, m_array_repository, true, (void *) this);
@ -1505,6 +1539,11 @@ Layout::register_cell_name (const char *name, cell_index_type ci)
void void
Layout::rename_cell (cell_index_type id, const char *name) Layout::rename_cell (cell_index_type id, const char *name)
{ {
static const char *anonymous_name = "";
if (! name) {
name = anonymous_name;
}
tl_assert (id < m_cell_names.size ()); tl_assert (id < m_cell_names.size ());
if (strcmp (m_cell_names [id], name) != 0) { if (strcmp (m_cell_names [id], name) != 0) {
@ -1521,7 +1560,10 @@ Layout::rename_cell (cell_index_type id, const char *name)
delete [] m_cell_names [id]; delete [] m_cell_names [id];
m_cell_names [id] = cp; m_cell_names [id] = cp;
// NOTE: anonymous cells (empty name string) are not registered in the cell name map
if (*cp != 0) {
m_cell_map.insert (std::make_pair (cp, id)); m_cell_map.insert (std::make_pair (cp, id));
}
// to enforce a redraw and a rebuild // to enforce a redraw and a rebuild
cell_name_changed (); cell_name_changed ();
@ -1690,27 +1732,120 @@ Layout::cleanup (const std::set<db::cell_index_type> &keep)
return; return;
} }
if (tl::verbosity () >= 30) {
tl::info << "Cleaning up layout ..";
}
// Do some polishing of the proxies - sometimes, specifically when resolving indirect library references,
// different proxies to the same library object exist. We can identify them and clean them up, so there
// is a single reference. We can also try to ensure that cell names reflect the library cell names.
// The latter is good for LVS for example.
{
update();
db::LayoutLocker locker (this);
// join library proxies pointing to the same object
for (auto c = m_lib_proxy_map.begin (); c != m_lib_proxy_map.end (); ) {
auto c0 = c++;
size_t n = 1;
while (c != m_lib_proxy_map.end () && c->first == c0->first) {
++n;
++c;
}
if (n > 1) {
auto cc = c0;
++cc;
while (cc != c) {
if (keep.find (cc->second) == keep.end ()) {
if (tl::verbosity () >= 30) {
tl::info << "Joining lib proxy " << cell_name (cc->second) << " into " << cell_name (c0->second);
}
replace_instances_of (cc->second, c0->second);
}
++cc;
}
}
}
// join cold proxies pointing to the same object
for (auto c = m_cold_proxy_map.begin (); c != m_cold_proxy_map.end (); ) {
auto c0 = c++;
size_t n = 1;
while (c != m_cold_proxy_map.end () && c->first == c0->first) {
++n;
++c;
}
if (n > 1) {
auto cc = c0;
++cc;
while (cc != c) {
if (keep.find (cc->second) == keep.end ()) {
if (tl::verbosity () >= 30) {
tl::info << "Joining cold proxy " << cell_name (cc->second) << " into " << cell_name (c0->second);
}
replace_instances_of (cc->second, c0->second);
}
++cc;
}
}
}
}
std::set<cell_index_type> cells_to_delete;
// deleting cells may create new top cells which need to be deleted as well, hence we iterate // deleting cells may create new top cells which need to be deleted as well, hence we iterate
// until there are no more cells to delete // until there are no more cells to delete
while (true) { while (true) {
// delete all cells that are top cells and are proxies. Those cells are proxies no longer required. // delete all cells that are top cells and are proxies. Those cells are proxies no longer required.
std::set<cell_index_type> cells_to_delete;
for (top_down_iterator c = begin_top_down (); c != end_top_cells (); ++c) { for (top_down_iterator c = begin_top_down (); c != end_top_cells (); ++c) {
if (cell (*c).is_proxy ()) { if (cell (*c).is_proxy () && keep.find (*c) == keep.end ()) {
cells_to_delete.insert (*c); cells_to_delete.insert (*c);
} }
} }
for (std::set<db::cell_index_type>::const_iterator k = keep.begin (); k != keep.end (); ++k) {
cells_to_delete.erase (*k);
}
if (cells_to_delete.empty ()) { if (cells_to_delete.empty ()) {
break; break;
} }
delete_cells (cells_to_delete); delete_cells (cells_to_delete);
cells_to_delete.clear ();
}
// Try to ensure that cell names reflect the library cell names. The latter is good for LVS for example.
for (auto c = m_lib_proxy_map.begin (); c != m_lib_proxy_map.end (); ++c) {
std::string bn = cell (c->second).get_basic_name ();
if (bn != cell_name (c->second) && ! cell_by_name (bn.c_str ()).first) {
if (tl::verbosity () >= 30) {
tl::info << "Renaming lib proxy " << cell_name (c->second) << " to " << bn;
}
rename_cell (c->second, bn.c_str ());
}
}
for (auto c = m_cold_proxy_map.begin (); c != m_cold_proxy_map.end (); ++c) {
std::string bn = cell (c->second).get_basic_name ();
if (bn != cell_name (c->second) && ! cell_by_name (bn.c_str ()).first) {
if (tl::verbosity () >= 30) {
tl::info << "Renaming cold proxy " << cell_name (c->second) << " to " << bn;
}
rename_cell (c->second, bn.c_str ());
}
} }
} }
@ -2987,6 +3122,14 @@ Layout::fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrC
void void
Layout::restore_proxies (ImportLayerMapping *layer_mapping) 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; std::vector<db::ColdProxy *> cold_proxies;
@ -3004,9 +3147,7 @@ Layout::restore_proxies (ImportLayerMapping *layer_mapping)
} }
} }
if (needs_cleanup) { return needs_cleanup;
cleanup ();
}
} }
bool bool
@ -3145,13 +3286,32 @@ Layout::variant_name (cell_index_type cell_index) const
void void
Layout::register_lib_proxy (db::LibraryProxy *lib_proxy) Layout::register_lib_proxy (db::LibraryProxy *lib_proxy)
{ {
m_lib_proxy_map.insert (std::make_pair (std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ()), lib_proxy->Cell::cell_index ())); auto key = std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ());
auto l = m_lib_proxy_map.find (key);
while (l != m_lib_proxy_map.end () && l->first == key) {
if (l->second == lib_proxy->Cell::cell_index ()) {
return;
}
++l;
}
m_lib_proxy_map.insert (std::make_pair (key, lib_proxy->Cell::cell_index ()));
} }
void void
Layout::unregister_lib_proxy (db::LibraryProxy *lib_proxy) Layout::unregister_lib_proxy (db::LibraryProxy *lib_proxy)
{ {
m_lib_proxy_map.erase (std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ())); auto key = std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ());
auto l = m_lib_proxy_map.find (key);
while (l != m_lib_proxy_map.end () && l->first == key) {
if (l->second == lib_proxy->Cell::cell_index ()) {
m_lib_proxy_map.erase (l);
break;
}
++l;
}
} }
void void
@ -3171,9 +3331,13 @@ Layout::get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_t
cell_index_type cell_index_type
Layout::get_lib_proxy (Library *lib, cell_index_type cell_index) Layout::get_lib_proxy (Library *lib, cell_index_type cell_index)
{ {
lib_proxy_map::const_iterator lp = m_lib_proxy_map.find (std::make_pair (lib->get_id (), cell_index)); auto key = std::make_pair (lib->get_id (), cell_index);
if (lp != m_lib_proxy_map.end ()) {
lib_proxy_map::const_iterator lp = m_lib_proxy_map.find (key);
if (lp != m_lib_proxy_map.end () && lp->first == key) {
return lp->second; return lp->second;
} else { } else {
// create a new unique name // create a new unique name
@ -3204,9 +3368,54 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index)
} }
} }
void
Layout::register_cold_proxy (db::ColdProxy *cold_proxy)
{
auto l = m_cold_proxy_map.find (cold_proxy->context_info ());
while (l != m_cold_proxy_map.end () && l->first == cold_proxy->context_info ()) {
if (l->second == cold_proxy->Cell::cell_index ()) {
return;
}
++l;
}
m_cold_proxy_map.insert (std::make_pair (cold_proxy->context_info (), cold_proxy->Cell::cell_index ()));
}
void
Layout::unregister_cold_proxy (db::ColdProxy *cold_proxy)
{
auto l = m_cold_proxy_map.find (cold_proxy->context_info ());
while (l != m_cold_proxy_map.end () && l->first == cold_proxy->context_info ()) {
if (l->second == cold_proxy->Cell::cell_index ()) {
m_cold_proxy_map.erase (l);
break;
}
++l;
}
}
std::pair<bool, cell_index_type>
Layout::find_cold_proxy (const db::LayoutOrCellContextInfo &info)
{
cold_proxy_map::const_iterator lp = m_cold_proxy_map.find (info);
if (lp != m_cold_proxy_map.end () && lp->first == info) {
return std::make_pair (true, lp->second);
} else {
return std::make_pair (false, 0);
}
}
cell_index_type cell_index_type
Layout::create_cold_proxy (const db::LayoutOrCellContextInfo &info) Layout::create_cold_proxy (const db::LayoutOrCellContextInfo &info)
{ {
cold_proxy_map::const_iterator lp = m_cold_proxy_map.find (info);
if (lp != m_cold_proxy_map.end () && lp->first == info) {
return lp->second;
} else {
// create a new unique name // create a new unique name
std::string b; std::string b;
if (! info.cell_name.empty ()) { if (! info.cell_name.empty ()) {
@ -3233,6 +3442,8 @@ Layout::create_cold_proxy (const db::LayoutOrCellContextInfo &info)
} }
return new_index; return new_index;
}
} }
void void

View File

@ -62,6 +62,7 @@ class PCellDeclaration;
class PCellHeader; class PCellHeader;
class Library; class Library;
class LibraryProxy; class LibraryProxy;
class ColdProxy;
class CellMapping; class CellMapping;
class LayerMapping; class LayerMapping;
class Region; class Region;
@ -427,6 +428,9 @@ struct DB_PUBLIC LayoutOrCellContextInfo
std::map<std::string, tl::Variant> pcell_parameters; std::map<std::string, tl::Variant> pcell_parameters;
std::map<std::string, std::pair<tl::Variant, std::string> > meta_info; std::map<std::string, std::pair<tl::Variant, std::string> > meta_info;
bool operator== (const LayoutOrCellContextInfo &other) const;
bool operator< (const LayoutOrCellContextInfo &other) const;
static LayoutOrCellContextInfo deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to); static LayoutOrCellContextInfo deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to);
void serialize (std::vector<std::string> &strings); void serialize (std::vector<std::string> &strings);
@ -469,7 +473,8 @@ public:
typedef db::pcell_id_type pcell_id_type; typedef db::pcell_id_type pcell_id_type;
typedef std::map<std::string, pcell_id_type> pcell_name_map; typedef std::map<std::string, pcell_id_type> pcell_name_map;
typedef pcell_name_map::const_iterator pcell_iterator; typedef pcell_name_map::const_iterator pcell_iterator;
typedef std::map<std::pair<lib_id_type, cell_index_type>, cell_index_type> lib_proxy_map; typedef std::multimap<std::pair<lib_id_type, cell_index_type>, cell_index_type> lib_proxy_map;
typedef std::multimap<db::LayoutOrCellContextInfo, cell_index_type> cold_proxy_map;
typedef LayerIterator layer_iterator; typedef LayerIterator layer_iterator;
typedef size_t meta_info_name_id_type; typedef size_t meta_info_name_id_type;
typedef std::map<meta_info_name_id_type, MetaInfo> meta_info_map; typedef std::map<meta_info_name_id_type, MetaInfo> meta_info_map;
@ -1003,6 +1008,12 @@ public:
*/ */
void get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping = 0, bool retain_layout = false); void get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping = 0, bool retain_layout = false);
/**
* @brief Find an existing cold proxy for a given context
* @return A pair of success flag and cell index of the proxy
*/
std::pair<bool, cell_index_type> find_cold_proxy (const db::LayoutOrCellContextInfo &info);
/** /**
* @brief Creates a cold proxy representing the given context information * @brief Creates a cold proxy representing the given context information
*/ */
@ -1115,7 +1126,16 @@ public:
* Library updates may enabled lost connections which are help in cold proxies. This method will recover * Library updates may enabled lost connections which are help in cold proxies. This method will recover
* these connections. * 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 * @brief Replaces the given cell index with the new cell
@ -1830,6 +1850,20 @@ public:
*/ */
void unregister_lib_proxy (db::LibraryProxy *lib_proxy); void unregister_lib_proxy (db::LibraryProxy *lib_proxy);
/**
* @brief Register a cold proxy
*
* This method is used by ColdProxy to register itself.
*/
void register_cold_proxy (db::ColdProxy *cold_proxy);
/**
* @brief Unregister a cold proxy
*
* This method is used by ColdProxy to unregister itself.
*/
void unregister_cold_proxy (db::ColdProxy *cold_proxy);
/** /**
* @brief Gets the editable status of this layout * @brief Gets the editable status of this layout
* *
@ -2144,6 +2178,7 @@ private:
std::vector<pcell_header_type *> m_pcells; std::vector<pcell_header_type *> m_pcells;
pcell_name_map m_pcell_ids; pcell_name_map m_pcell_ids;
lib_proxy_map m_lib_proxy_map; lib_proxy_map m_lib_proxy_map;
cold_proxy_map m_cold_proxy_map;
bool m_do_cleanup; bool m_do_cleanup;
bool m_editable; bool m_editable;
std::map<std::string, meta_info_name_id_type> m_meta_info_name_map; std::map<std::string, meta_info_name_id_type> m_meta_info_name_map;

View File

@ -26,20 +26,24 @@
#include "dbPCellDeclaration.h" #include "dbPCellDeclaration.h"
#include "dbPCellVariant.h" #include "dbPCellVariant.h"
#include "dbLibraryManager.h" #include "dbLibraryManager.h"
#include "tlTimer.h"
#include <limits> #include <limits>
namespace db namespace db
{ {
Library::Library() Library::Library ()
: m_id (std::numeric_limits<lib_id_type>::max ()), m_layout (true) : m_id (std::numeric_limits<lib_id_type>::max ()), m_layout (true), m_replicate (true)
{ {
m_layout.set_library (this); m_layout.set_library (this);
} }
Library::Library(const Library &d) Library::Library (const Library &d)
: gsi::ObjectBase (), tl::Object (), m_name (d.m_name), m_description (d.m_description), m_id (std::numeric_limits<lib_id_type>::max ()), m_layout (d.m_layout) : gsi::ObjectBase (), tl::Object (),
m_name (d.m_name), m_description (d.m_description),
m_id (std::numeric_limits<lib_id_type>::max ()), m_layout (d.m_layout),
m_replicate (d.m_replicate)
{ {
m_layout.set_library (this); m_layout.set_library (this);
} }
@ -64,6 +68,12 @@ Library::for_technologies () const
return ! m_technologies.empty (); return ! m_technologies.empty ();
} }
void
Library::set_technologies (const std::set<std::string> &t)
{
m_technologies = t;
}
void void
Library::set_technology (const std::string &t) Library::set_technology (const std::string &t)
{ {
@ -108,12 +118,14 @@ Library::unregister_proxy (db::LibraryProxy *lib_proxy, db::Layout *ly)
if (! --c->second) { if (! --c->second) {
db::cell_index_type ci = c->first; db::cell_index_type ci = c->first;
m_refcount.erase (c); m_refcount.erase (c);
if (layout ().is_valid_cell_index (ci)) {
// remove cells which are itself proxies and are no longer used // remove cells which are itself proxies and are no longer used
db::Cell *lib_cell = &layout ().cell (ci); db::Cell *lib_cell = &layout ().cell (ci);
if (lib_cell && lib_cell->is_proxy () && lib_cell->parent_cells () == 0) { if (lib_cell && lib_cell->is_proxy () && lib_cell->parent_cells () == 0) {
layout ().delete_cell (ci); layout ().delete_cell (ci);
} }
} }
}
retired_state_changed_event (); retired_state_changed_event ();
} }
} }
@ -145,6 +157,12 @@ Library::is_retired (const db::cell_index_type library_cell_index) const
return (i != m_refcount.end () && j != m_retired_count.end () && i->second == j->second); return (i != m_refcount.end () && j != m_retired_count.end () && i->second == j->second);
} }
void
Library::set_replicate (bool f)
{
m_replicate = f;
}
void void
Library::rename (const std::string &name) Library::rename (const std::string &name)
{ {
@ -164,37 +182,83 @@ Library::rename (const std::string &name)
void void
Library::refresh () Library::refresh ()
{ {
std::string name = reload (); refresh_without_restore ();
rename (name);
layout ().refresh (); // proxies need to be restored in a special case when a library has a proxy to itself.
// The layout readers will not be able to create library references in this case.
layout ().restore_proxies ();
}
void
Library::refresh_without_restore ()
{
db::LibraryManager::instance ().unregister_lib (this);
try {
m_name = reload ();
} catch (...) {
// fallback - leave the library, but with empty layout
layout ().clear ();
db::LibraryManager::instance ().register_lib (this);
throw;
}
// re-register, potentially under the new name
db::LibraryManager::instance ().register_lib (this);
// This is basically only needed for coerce_parameters in a persistent way.
// During "register_lib", this is already called in PCell update, but cannot be persisted then.
remap_to (this); remap_to (this);
} }
void void
Library::remap_to (db::Library *other) Library::remap_to (db::Library *other, db::Layout *original_layout)
{ {
if (! original_layout) {
original_layout = &layout ();
}
// Hint: in the loop over the referrers we might unregister (delete from m_referrers) a referrer because no more cells refer to us. // 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. // 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) { 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 // Remember the layouts that will finally need a cleanup
std::set<db::Layout *> needs_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) {
any = false;
for (std::vector<db::Layout *>::const_iterator r = referrers.begin (); r != referrers.end (); ++r) {
std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> > pcells_to_map; std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> > pcells_to_map;
std::vector<db::LibraryProxy *> lib_cells_to_map; std::vector<db::LibraryProxy *> lib_cells_to_map;
for (db::Layout::iterator c = r->first->begin (); c != r->first->end (); ++c) { for (auto c = (*r)->begin (); c != (*r)->end (); ++c) {
db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (&*c); db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (c.operator-> ());
if (lib_proxy && lib_proxy->lib_id () == get_id ()) { if (lib_proxy && lib_proxy->lib_id () == get_id ()) {
db::Cell *lib_cell = &layout ().cell (lib_proxy->library_cell_index ()); 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); db::PCellVariant *lib_pcell = dynamic_cast <db::PCellVariant *> (lib_cell);
if (lib_pcell) { if (lib_pcell) {
pcells_to_map.push_back (std::make_pair (lib_proxy, lib_pcell)); pcells_to_map.push_back (std::make_pair (lib_proxy, lib_pcell));
@ -202,7 +266,7 @@ Library::remap_to (db::Library *other)
lib_cells_to_map.push_back (lib_proxy); lib_cells_to_map.push_back (lib_proxy);
} }
needs_cleanup.insert (r->first); needs_cleanup.insert (*r);
} }
@ -218,26 +282,26 @@ Library::remap_to (db::Library *other)
std::pair<bool, pcell_id_type> pn (false, 0); std::pair<bool, pcell_id_type> pn (false, 0);
if (other) { if (other) {
pn = other->layout ().pcell_by_name (lp->first->get_basic_name ().c_str ()); pn = other->layout ().pcell_by_name (original_layout->cell (lp->first->library_cell_index ()).get_basic_name ().c_str ());
} }
if (! pn.first) { if (! pn.first) {
// substitute by a cold proxy // substitute by a cold proxy
db::LayoutOrCellContextInfo info; db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info); (*r)->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci); (*r)->create_cold_proxy_as (info, ci);
} else { } else {
const db::PCellDeclaration *old_pcell_decl = layout ().pcell_declaration (lib_pcell->pcell_id ()); 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); const db::PCellDeclaration *new_pcell_decl = other->layout ().pcell_declaration (pn.second);
if (! old_pcell_decl || ! new_pcell_decl) { if (! old_pcell_decl || ! new_pcell_decl) {
// substitute by a cold proxy // substitute by a cold proxy
db::LayoutOrCellContextInfo info; db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info); (*r)->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci); (*r)->create_cold_proxy_as (info, ci);
} else { } else {
@ -267,18 +331,23 @@ Library::remap_to (db::Library *other)
std::pair<bool, cell_index_type> cn (false, 0); std::pair<bool, cell_index_type> cn (false, 0);
if (other) { if (other) {
cn = other->layout ().cell_by_name ((*lp)->get_basic_name ().c_str ()); 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 // substitute by a cold proxy
db::LayoutOrCellContextInfo info; db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info); (*r)->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci); (*r)->create_cold_proxy_as (info, ci);
} else { } else {
if ((*lp)->lib_id () != other->get_id () || (*lp)->library_cell_index () != cn.second) {
// we potentially need another iteration
any = true;
}
(*lp)->remap (other->get_id (), cn.second); (*lp)->remap (other->get_id (), cn.second);
} }
@ -287,6 +356,8 @@ Library::remap_to (db::Library *other)
} }
}
// Do a cleanup later since the referrers now might have invalid proxy instances // Do a cleanup later since the referrers now might have invalid proxy instances
for (std::set<db::Layout *>::const_iterator c = needs_cleanup.begin (); c != needs_cleanup.end (); ++c) { for (std::set<db::Layout *>::const_iterator c = needs_cleanup.begin (); c != needs_cleanup.end (); ++c) {
(*c)->cleanup (); (*c)->cleanup ();

View File

@ -53,12 +53,12 @@ public:
/** /**
* @brief The constructor * @brief The constructor
*/ */
Library(); Library ();
/** /**
* @brief Copy constructor * @brief Copy constructor
*/ */
Library(const Library &); Library (const Library &);
/** /**
* @brief The destructor * @brief The destructor
@ -138,6 +138,13 @@ public:
*/ */
bool for_technologies () const; bool for_technologies () const;
/**
* @brief Sets the technology names this library is associated with
*
* This will reset the list of technologies to this set.
*/
void set_technologies (const std::set<std::string> &t);
/** /**
* @brief Sets the technology name this library is associated with * @brief Sets the technology name this library is associated with
* *
@ -172,6 +179,27 @@ public:
m_description = description; m_description = description;
} }
/**
* @brief Sets a value indicating whether the library produces replicas
*
* If this value is true (the default), layout written will include the
* actual layout of a library cell (replica). With this, it is possible
* to regenerate the layout without actually having the library at the
* cost of additional bytes in the file.
*
* Setting this flag to false avoids this replication, but a layout
* cannot be regenerated without having this library.
*/
void set_replicate (bool f);
/**
* @brief Gets a value indicating whether the library produces replicas
*/
bool replicate () const
{
return m_replicate;
}
/** /**
* @brief Getter for the library Id property * @brief Getter for the library Id property
*/ */
@ -225,6 +253,13 @@ public:
*/ */
void refresh (); void refresh ();
/**
* @brief Refreshes the library on all clients without restoring proxies
*
* This method is intended to be used internally for bulk refreshes.
*/
void refresh_without_restore ();
/** /**
* @brief Renames the library * @brief Renames the library
* *
@ -237,8 +272,11 @@ public:
* @brief Remap the library proxies to a different library * @brief Remap the library proxies to a different library
* *
* After remapping, "other" can replace "this". * After remapping, "other" can replace "this".
* When calling with "other=this", a pointer to the original
* layout needs to be supplied, because in that case, the
* layout of "this" is already replaced.
*/ */
void remap_to (db::Library *other); void remap_to (db::Library *other, Layout *original_layout = 0);
/** /**
* @brief This event is fired if proxies get retired on unretired * @brief This event is fired if proxies get retired on unretired
@ -253,6 +291,7 @@ private:
db::Layout m_layout; db::Layout m_layout;
std::map<db::Layout *, int> m_referrers; std::map<db::Layout *, int> m_referrers;
std::map<db::cell_index_type, int> m_refcount, m_retired_count; std::map<db::cell_index_type, int> m_refcount, m_retired_count;
bool m_replicate;
// no copying. // no copying.
Library &operator=(const Library &); Library &operator=(const Library &);

View File

@ -38,7 +38,7 @@ namespace db
static LibraryManager *sp_instance (0); static LibraryManager *sp_instance (0);
LibraryManager & DB_PUBLIC LibraryManager &
LibraryManager::instance () LibraryManager::instance ()
{ {
if (sp_instance == 0) { if (sp_instance == 0) {
@ -249,13 +249,38 @@ LibraryManager::register_lib (Library *library)
// "restore_proxies" takes care not to re-substitute cold proxies. // "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 ()); 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) { 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) { // Sort for deterministic order of resolution
(*l)->restore_proxies (0); 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 // issue the change notification
@ -284,9 +309,27 @@ LibraryManager::lib_internal (lib_id_type id) const
void void
LibraryManager::refresh_all () LibraryManager::refresh_all ()
{ {
// NOTE: libraries may get deleted during the refresh, so better use weak pointers here
// to track lifetime. Libraries appearing are not considered.
std::vector<tl::weak_ptr<Library> > libs;
libs.reserve (m_libs.size ());
for (auto l = m_libs.begin (); l != m_libs.end (); ++l) {
libs.push_back (tl::weak_ptr<Library> (*l));
}
for (auto l = libs.begin (); l != libs.end (); ++l) {
if (l->get ()) {
(*l)->refresh_without_restore ();
}
}
// restore proxies on all libraries - as the refresh happens in random order, dependencies
// are not necessarily resolved. This is done here.
for (std::vector<Library *>::iterator l = m_libs.begin (); l != m_libs.end (); ++l) { for (std::vector<Library *>::iterator l = m_libs.begin (); l != m_libs.end (); ++l) {
if (*l) { if (*l) {
(*l)->refresh (); (*l)->layout ().restore_proxies ();
} }
} }
} }

View File

@ -22,6 +22,7 @@
#include "dbLibraryProxy.h" #include "dbLibraryProxy.h"
#include "dbColdProxy.h"
#include "dbLibraryManager.h" #include "dbLibraryManager.h"
#include "dbLibrary.h" #include "dbLibrary.h"
#include "dbLayout.h" #include "dbLayout.h"
@ -187,25 +188,6 @@ LibraryProxy::get_layer_indices (db::Layout &layout, db::ImportLayerMapping *lay
return m_layer_indices; return m_layer_indices;
} }
class LibraryCellIndexMapper
{
public:
LibraryCellIndexMapper (Layout &layout, Library *lib)
: mp_lib (lib), mp_layout (&layout)
{
// .. nothing yet ..
}
cell_index_type operator() (cell_index_type cell_index_in_lib)
{
return mp_layout->get_lib_proxy (mp_lib, cell_index_in_lib);
}
private:
Library *mp_lib;
Layout *mp_layout;
};
void void
LibraryProxy::update (db::ImportLayerMapping *layer_mapping) LibraryProxy::update (db::ImportLayerMapping *layer_mapping)
{ {
@ -215,30 +197,63 @@ LibraryProxy::update (db::ImportLayerMapping *layer_mapping)
Library *lib = LibraryManager::instance ().lib (lib_id ()); Library *lib = LibraryManager::instance ().lib (lib_id ());
const db::Cell &source_cell = lib->layout ().cell (library_cell_index ()); const db::Cell &source_cell = lib->layout ().cell (library_cell_index ());
db::ICplxTrans tr;
bool need_transform = false;
if (fabs (layout ()->dbu () - lib->layout ().dbu ()) > 1e-6) {
need_transform = true;
tr = db::ICplxTrans (lib->layout ().dbu () / layout ()->dbu ());
}
clear_shapes (); clear_shapes ();
clear_insts (); clear_insts ();
for (unsigned int l = 0; l < lib->layout ().layers (); ++l) { for (unsigned int l = 0; l < lib->layout ().layers (); ++l) {
if (layer_indices [l] >= 0) { if (layer_indices [l] >= 0) {
shapes ((unsigned int) layer_indices [l]).assign_transformed (source_cell.shapes (l), tr); shapes ((unsigned int) layer_indices [l]).assign_transformed (source_cell.shapes (l), db::ICplxTrans (lib->layout ().dbu () / layout ()->dbu ()));
} }
} }
LibraryCellIndexMapper cell_index_mapper (*layout (), lib); for (Cell::const_iterator i = source_cell.begin (); ! i.at_end (); ++i) {
for (Cell::const_iterator inst = source_cell.begin (); !inst.at_end (); ++inst) { db::CellInstArray inst = i->cell_inst ();
db::Instance new_inst = insert (*inst, cell_index_mapper);
if (need_transform) { Library *real_lib = lib;
replace (new_inst, new_inst.cell_inst ().transformed_into (tr)); cell_index_type real_cil = inst.object ().cell_index ();
// use the "final lib", so we refer to the actual lib instead
// of building chains of lib references
LibraryProxy *lp;
while ((lp = dynamic_cast<LibraryProxy *> (&real_lib->layout ().cell (real_cil))) != 0) {
real_cil = lp->library_cell_index ();
real_lib = db::LibraryManager::instance ().lib (lp->lib_id ());
} }
ColdProxy *cp = dynamic_cast<ColdProxy *> (&real_lib->layout ().cell (real_cil));
if (cp) {
// The final item is a cold proxy ("<defunct>" cell) - treat it like
// a library proxy as it may become one in the future, but replace it
// by a cold proxy now.
auto p = layout ()->find_cold_proxy (cp->context_info ());
if (p.first) {
// reuse existing proxy
inst.object ().cell_index (p.second);
} else {
// create a new proxy, reusing the first library proxy's replica
inst.object ().cell_index (layout ()->get_lib_proxy (real_lib, real_cil));
layout ()->create_cold_proxy_as (cp->context_info (), inst.object ().cell_index ());
} }
} else {
inst.object ().cell_index (layout ()->get_lib_proxy (real_lib, real_cil));
}
inst.transform_into (db::ICplxTrans (lib->layout ().dbu () / layout ()->dbu ()));
insert (inst);
}
}
bool
LibraryProxy::can_skip_replica () const
{
const Library *lib = LibraryManager::instance ().lib (lib_id ());
return lib && ! lib->replicate ();
} }
std::string std::string

View File

@ -93,6 +93,14 @@ public:
return true; return true;
} }
/**
* @brief Gets a value indicating that this cell is a replica that can be skipped
*
* This attribute is evaluated by file writers to skip cell replicas for
* library cells that do not want to replicated.
*/
virtual bool can_skip_replica () const;
/** /**
* @brief Gets the basic name * @brief Gets the basic name
* *
@ -129,12 +137,12 @@ public:
/** /**
* @brief Reimplemented from Cell: unregisters the proxy at the layout * @brief Reimplemented from Cell: unregisters the proxy at the layout
*/ */
void unregister (); virtual void unregister ();
/** /**
* @brief Reimplemented from Cell: reregisters the proxy at the layout * @brief Reimplemented from Cell: reregisters the proxy at the layout
*/ */
void reregister (); virtual void reregister ();
private: private:
lib_id_type m_lib_id; lib_id_type m_lib_id;

View File

@ -57,7 +57,7 @@ SaveLayoutOptions::operator= (const SaveLayoutOptions &d)
m_format = d.m_format; m_format = d.m_format;
m_layers = d.m_layers; m_layers = d.m_layers;
m_cells = d.m_cells; m_cells = d.m_cells;
m_implied_childred = d.m_implied_childred; m_implied_children = d.m_implied_children;
m_all_layers = d.m_all_layers; m_all_layers = d.m_all_layers;
m_all_cells = d.m_all_cells; m_all_cells = d.m_all_cells;
m_dbu = d.m_dbu; m_dbu = d.m_dbu;
@ -190,7 +190,7 @@ SaveLayoutOptions::add_cell (db::cell_index_type cell_index)
{ {
m_all_cells = false; m_all_cells = false;
m_cells.insert (cell_index); m_cells.insert (cell_index);
m_implied_childred.insert (cell_index); m_implied_children.insert (cell_index);
} }
void void
@ -205,7 +205,7 @@ SaveLayoutOptions::clear_cells ()
{ {
m_all_cells = false; m_all_cells = false;
m_cells.clear (); m_cells.clear ();
m_implied_childred.clear (); m_implied_children.clear ();
} }
void void
@ -213,7 +213,7 @@ SaveLayoutOptions::select_all_cells ()
{ {
m_all_cells = true; m_all_cells = true;
m_cells.clear (); m_cells.clear ();
m_implied_childred.clear (); m_implied_children.clear ();
} }
void void
@ -341,23 +341,77 @@ SaveLayoutOptions::get_valid_layers (const db::Layout &layout, std::vector <std:
} }
} }
static void
collect_called_cells_unskipped (db::cell_index_type ci, const db::Layout &layout, std::set<cell_index_type> &called)
{
const db::Cell &c = layout.cell (ci);
if (c.can_skip_replica ()) {
return;
}
for (auto cc = c.begin_child_cells (); ! cc.at_end (); ++cc) {
if (called.find (*cc) == called.end () && layout.is_valid_cell_index (*cc)) {
called.insert (*cc);
collect_called_cells_unskipped (*cc, layout, called);
}
}
}
void void
SaveLayoutOptions::get_cells (const db::Layout &layout, std::set <db::cell_index_type> &cells, const std::vector <std::pair <unsigned int, db::LayerProperties> > &valid_layers, bool require_unique_names) const SaveLayoutOptions::get_cells (const db::Layout &layout, std::set <db::cell_index_type> &cells, const std::vector <std::pair <unsigned int, db::LayerProperties> > &valid_layers, bool require_unique_names) const
{ {
bool has_context = m_write_context_info;
for (tl::Registrar<db::StreamFormatDeclaration>::iterator fmt = tl::Registrar<db::StreamFormatDeclaration>::begin (); fmt != tl::Registrar<db::StreamFormatDeclaration>::end (); ++fmt) {
if (fmt->format_name () == m_format) {
if (! fmt->supports_context ()) {
has_context = false;
}
break;
}
}
if (m_all_cells) { if (m_all_cells) {
bool has_skipped_replica = false;
// check if we have skipped replicas
if (has_context) {
for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end () && ! has_skipped_replica; ++cell) {
has_skipped_replica = cell->can_skip_replica ();
}
}
// with skipped replicas start again and skip child cells of such cells
// unless they are needed in other places.
if (has_skipped_replica) {
for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) { for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) {
if (cell->is_top ()) {
cells.insert (cell->cell_index ()); cells.insert (cell->cell_index ());
collect_called_cells_unskipped (cell->cell_index (), layout, cells);
}
}
} else {
for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end () && ! has_skipped_replica; ++cell) {
cells.insert (cell->cell_index ());
}
} }
} else { } else {
for (std::set <db::cell_index_type>::const_iterator c = m_cells.begin (); c != m_cells.end (); ++c) { for (std::set <db::cell_index_type>::const_iterator c = m_cells.begin (); c != m_cells.end (); ++c) {
cells.insert (*c); cells.insert (*c);
if (m_implied_childred.find (*c) != m_implied_childred.end ()) { if (m_implied_children.find (*c) != m_implied_children.end ()) {
if (has_context) {
collect_called_cells_unskipped (*c, layout, cells);
} else {
layout.cell (*c).collect_called_cells (cells); layout.cell (*c).collect_called_cells (cells);
} }
} }
}
} }

View File

@ -449,7 +449,7 @@ private:
std::string m_format; std::string m_format;
std::map<unsigned int, db::LayerProperties> m_layers; std::map<unsigned int, db::LayerProperties> m_layers;
std::set<db::cell_index_type> m_cells; std::set<db::cell_index_type> m_cells;
std::set<db::cell_index_type> m_implied_childred; std::set<db::cell_index_type> m_implied_children;
bool m_all_layers; bool m_all_layers;
bool m_all_cells; bool m_all_cells;
double m_dbu; double m_dbu;

View File

@ -109,6 +109,11 @@ public:
*/ */
virtual bool can_write () const = 0; virtual bool can_write () const = 0;
/**
* @brief Returns true, when the format supports a context (e.g. PCell parameters, Library references)
*/
virtual bool supports_context () const = 0;
/** /**
* @brief Delivers the XMLElement object that represents the reader options within a technology XML tree * @brief Delivers the XMLElement object that represents the reader options within a technology XML tree
* *

View File

@ -135,6 +135,42 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std::
db::Reader reader (stream); db::Reader reader (stream);
reader.read (layout_au, options); reader.read (layout_au, options);
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, equal = db::compare_layouts (*subject, layout_au,
(n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) (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 & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0)
@ -142,6 +178,9 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std::
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta) | ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta)
/*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/ /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/
, tolerance, 100 /*max diff lines*/); , tolerance, 100 /*max diff lines*/);
}
if (equal && n > 0) { if (equal && n > 0) {
tl::info << tl::sprintf ("Found match on golden reference variant %s", fn); tl::info << tl::sprintf ("Found match on golden reference variant %s", fn);
} }

View File

@ -59,7 +59,8 @@ enum NormalizationMode
NoContext = 8, // write tmp file without context NoContext = 8, // write tmp file without context
AsPolygons = 16, // paths and boxes are treated as polygons AsPolygons = 16, // paths and boxes are treated as polygons
WithArrays = 32, // do not flatten arrays WithArrays = 32, // do not flatten arrays
WithMeta = 64 // with meta info WithMeta = 64, // with meta info
WithoutCellNames = 128 // smart cell name mapping
}; };
/** /**

View File

@ -29,6 +29,7 @@
#include "dbPCellDeclaration.h" #include "dbPCellDeclaration.h"
#include "dbLibrary.h" #include "dbLibrary.h"
#include "dbLibraryManager.h" #include "dbLibraryManager.h"
#include "dbFileBasedLibrary.h"
#include "tlLog.h" #include "tlLog.h"
namespace gsi namespace gsi
@ -156,11 +157,91 @@ static LibraryImpl *new_lib ()
return new LibraryImpl (); return new LibraryImpl ();
} }
static db::Library *library_from_file (const std::string &path, const std::string &name, const std::string &for_technology)
{
// Check if a library with this specification already is installed and reuse in that case.
if (! name.empty ()) {
db::FileBasedLibrary *old_lib = dynamic_cast<db::FileBasedLibrary *> (library_by_name (name, for_technology));
if (old_lib && old_lib->is_for_path (path)) {
old_lib->load ();
return old_lib;
}
}
std::unique_ptr<db::FileBasedLibrary> lib (new db::FileBasedLibrary (path, name));
lib->set_technology (for_technology);
std::string n = lib->load ();
db::Library *ret = lib.get ();
register_lib (lib.release (), n);
return ret;
}
static db::Library *library_from_files (const std::vector<std::string> &paths, const std::string &name, const std::string &for_technology)
{
if (paths.empty ()) {
throw tl::Exception (tl::to_string (tr ("At least one path must be given")));
}
// Check if a library with this specification already is installed and reuse in that case.
if (! name.empty ()) {
db::FileBasedLibrary *old_lib = dynamic_cast<db::FileBasedLibrary *> (library_by_name (name, for_technology));
if (old_lib && old_lib->is_for_paths (paths)) {
old_lib->load ();
return old_lib;
}
}
std::unique_ptr<db::FileBasedLibrary> lib (new db::FileBasedLibrary (paths.front (), name));
for (auto i = paths.begin () + 1; i != paths.end (); ++i) {
lib->merge_with_other_layout (*i);
}
lib->set_technology (for_technology);
std::string n = lib->load ();
db::Library *ret = lib.get ();
register_lib (lib.release (), n);
return ret;
}
/** /**
* @brief A basic implementation of the library * @brief A basic implementation of the library
*/ */
LibraryClass<db::Library> decl_Library ("db", "LibraryBase", LibraryClass<db::Library> decl_Library ("db", "LibraryBase",
gsi::method ("library_from_file", &library_from_file, gsi::arg ("path"), gsi::arg ("name", std::string (), "auto"), gsi::arg ("for_technology", std::string (), "none"),
"@brief Creates a library from a file\n"
"@param path The path to the file from which to create the library from.\n"
"@param name The name of the library. If empty, the name will be derived from the GDS LIBNAME or the file name.\n"
"@return The library object created. It is already registered with the name given or derived from the file.\n"
"\n"
"This method will create a \\Library object which is tied to a specific file. This object supports "
"automatic reloading when the \\Library#refresh method is called.\n"
"\n"
"If a file-based library with the same name and path is registered already, this method will not reload again "
"and return the library that was already registered.\n"
"\n"
"This convenience method has been added in version 0.30.8.\n"
) +
gsi::method ("library_from_files", &library_from_files, gsi::arg ("paths"), gsi::arg ("name", std::string (), "auto"), gsi::arg ("for_technology", std::string (), "none"),
"@brief Creates a library from a set of files\n"
"@param paths The paths to the files from which to create the library from. At least one file needs to be given.\n"
"@param name The name of the library. If empty, the name will be derived from the GDS LIBNAME or the file name.\n"
"@return The library object created. It is already registered with the name given or derived from the file.\n"
"\n"
"This method will create a \\Library object which is tied to several files. This object supports "
"automatic reloading when the \\Library#refresh method is called. The content of the files is merged "
"into the library. This is useful for example to create one library from a collection of files.\n"
"\n"
"This convenience method has been added in version 0.30.8.\n"
) +
gsi::method ("library_by_name", &library_by_name, gsi::arg ("name"), gsi::arg ("for_technology", std::string (), "unspecific"), gsi::method ("library_by_name", &library_by_name, gsi::arg ("name"), gsi::arg ("for_technology", std::string (), "unspecific"),
"@brief Gets a library by name\n" "@brief Gets a library by name\n"
"Returns the library object for the given name. If the name is not a valid library name, nil is returned.\n" "Returns the library object for the given name. If the name is not a valid library name, nil is returned.\n"
@ -213,6 +294,26 @@ LibraryClass<db::Library> decl_Library ("db", "LibraryBase",
"\n" "\n"
"This method has been introduced in version 0.30.5." "This method has been introduced in version 0.30.5."
) + ) +
gsi::method ("replicate=", &db::Library::set_replicate, gsi::arg ("flag"),
"@brief Sets a value indicating whether the library produces replicas\n"
"\n"
"If this value is true (the default), layout written will include the\n"
"actual layout of a library cell (replica). With this, it is possible\n"
"to regenerate the layout without actually having the library at the\n"
"cost of additional bytes in the file.\n"
"\n"
"Setting this flag to false avoids this replication, but a layout\n"
"cannot be regenerated without having this library.\n"
"\n"
"This attribute has been introduced in version 0.30.8."
) +
gsi::method ("replicate", &db::Library::replicate,
"@brief Gets a value indicating whether the library produces replicas\n"
"\n"
"See \\replicate= for a description of this attribute.\n"
"\n"
"This attribute has been introduced in version 0.30.8."
) +
gsi::method ("rename", &db::Library::rename, gsi::arg ("name"), gsi::method ("rename", &db::Library::rename, gsi::arg ("name"),
"@brief Renames the library\n" "@brief Renames the library\n"
"\n" "\n"

View File

@ -32,6 +32,8 @@
#include "dbReader.h" #include "dbReader.h"
#include "dbLayoutDiff.h" #include "dbLayoutDiff.h"
#include "dbTestSupport.h" #include "dbTestSupport.h"
#include "dbFileBasedLibrary.h"
#include "dbColdProxy.h"
#include "tlStream.h" #include "tlStream.h"
#include "tlStaticObjects.h" #include "tlStaticObjects.h"
#include "tlUnitTest.h" #include "tlUnitTest.h"
@ -698,3 +700,137 @@ TEST(6_issue996)
CHECKPOINT (); CHECKPOINT ();
db::compare_layouts (this, ly, tl::testdata () + "/gds/lib_test6b.gds", db::NormalizationMode (db::WriteGDS2 + db::NoContext)); 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 (6));
EXPECT_EQ (num_cells (layout), size_t (25));
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 (25));
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 (25));
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 (6));
EXPECT_EQ (num_cells (layout), size_t (25));
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 (12));
EXPECT_EQ (num_cells (layout), size_t (25));
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));
}

View File

@ -0,0 +1,92 @@
/*
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
*/
#include "dbLibraryManager.h"
#include "dbLibrary.h"
#include "dbFileBasedLibrary.h"
#include "tlUnitTest.h"
#include "tlFileUtils.h"
// map library to different file (aka reload/refresh)
TEST(1)
{
std::string pa = tl::testsrc () + "/testdata/gds/lib_a.gds";
std::string pb = tl::testsrc () + "/testdata/gds/lib_b.gds";
db::FileBasedLibrary *lib = new db::FileBasedLibrary (pa);
lib->load ();
lib->set_name ("LIB");
db::LibraryManager::instance ().register_lib (lib);
db::Layout l;
auto top_cell_index = l.add_cell ("TOP");
auto lib_proxy = l.get_lib_proxy (lib, lib->layout ().cell_by_name ("A").second);
l.cell (top_cell_index).insert (db::CellInstArray (db::CellInst (lib_proxy), db::Trans ()));
EXPECT_EQ (l.cell (top_cell_index).bbox ().to_string (), "(0,0;2000,2000)");
// switch to a different file and refresh
lib->set_paths (pb);
lib->refresh ();
EXPECT_EQ (l.cell (top_cell_index).bbox ().to_string (), "(-1000,-1000;1000,1000)");
db::LibraryManager::instance ().delete_lib (lib);
}
// merge multiple files into one library
TEST(2)
{
std::string pa = tl::testsrc () + "/testdata/gds/lib_a.gds";
std::string pb = tl::testsrc () + "/testdata/gds/lib_b.gds";
db::FileBasedLibrary *lib = new db::FileBasedLibrary (pa);
lib->merge_with_other_layout (pb);
lib->load ();
lib->set_name ("LIB");
db::LibraryManager::instance ().register_lib (lib);
{
db::Layout l;
auto top_cell_index = l.add_cell ("TOP");
auto lib_proxy = l.get_lib_proxy (lib, lib->layout ().cell_by_name ("A").second);
l.cell (top_cell_index).insert (db::CellInstArray (db::CellInst (lib_proxy), db::Trans ()));
// A comes from the first layout
EXPECT_EQ (l.cell (top_cell_index).bbox ().to_string (), "(0,0;2000,2000)");
}
{
db::Layout l;
auto top_cell_index = l.add_cell ("TOP");
auto lib_proxy = l.get_lib_proxy (lib, lib->layout ().cell_by_name ("Z").second);
l.cell (top_cell_index).insert (db::CellInstArray (db::CellInst (lib_proxy), db::Trans ()));
// Z comes from the second layout
EXPECT_EQ (l.cell (top_cell_index).bbox ().to_string (), "(0,0;100,100)");
}
db::LibraryManager::instance ().delete_lib (lib);
}

View File

@ -11,6 +11,7 @@ SOURCES = \
dbCompoundOperationTests.cc \ dbCompoundOperationTests.cc \
dbEdgeNeighborhoodTests.cc \ dbEdgeNeighborhoodTests.cc \
dbFillToolTests.cc \ dbFillToolTests.cc \
dbLibraryTests.cc \
dbLogTests.cc \ dbLogTests.cc \
dbObjectWithPropertiesTests.cc \ dbObjectWithPropertiesTests.cc \
dbPLCConvexDecompositionTests.cc \ dbPLCConvexDecompositionTests.cc \

View File

@ -6,6 +6,8 @@
<title>About Libraries</title> <title>About Libraries</title>
<keyword name="Library"/> <keyword name="Library"/>
<keyword name="Libraries"/> <keyword name="Libraries"/>
<keyword name="Lib Files"/>
<keyword name="Static Libraries"/>
<p> <p>
Starting with version 0.22, KLayout offers a library concept. Starting with version 0.22, KLayout offers a library concept.
@ -50,6 +52,22 @@
name of the library file minus the extension. name of the library file minus the extension.
</p> </p>
</li> </li>
<li><p><b>Layout files by declaration:</b>
Starting with version 0.30.8, KLayout supports declaration of static libraries by
a library definition file. This is an alternative mechanism to the "libraries" folder
and has a number of advantages:
</p>
<ul>
<li>Libraries can be referenced from any place in the file system</li>
<li>Library definition files can include other files and include environment variables</li>
<li>Additional attributes are available for declaration</li>
</ul>
<p>
This feature is limited to static libraries - i.e. libraries declared by library
definition files cannot include PCells. PCells are an exclusive feature of coded
libraries.
</p>
</li>
<li><b>Coded libraries:</b> <li><b>Coded libraries:</b>
Such libraries are provided by code, either through shared objects/DLL's or through Ruby code. Such libraries are provided by code, either through shared objects/DLL's or through Ruby code.
Basically such code has to provide a layout object containing the library cells. Basically such code has to provide a layout object containing the library cells.
@ -65,5 +83,186 @@
about packages. about packages.
</p> </p>
<p>
Static libraries, whether defined by a library declaration file or implicitly from the
"libraries" folder, are automatically reloaded, when the file changes. If this feature
is not desired, you can disable automatic reload in the Setup dialog and manually refresh
the libraries if required.
</p>
<h2>How libraries work</h2>
<p>
Unlike other systems, libraries are not external references for KLayout. Instead, a library
is essentially a copy source. When you use a library cell, it is copied into your
working layout and is weakly linked to the original place. You can refresh the library,
which will also refresh the copy. This process is called "replication".
</p>
<p>
This approach implies a memory adder, but has a number of benefits:
</p>
<ul>
<li>The working layout is "whole" - i.e. if a library vanishes or is not available
when transferring the layout to someone else, the layout is still functional. The
copy acts as a snapshot.
</li>
<li>
The independent copy allows for compensating database unit differences and layer details,
so the libraries can be made with a different database unit than the working layout for example.
</li>
<li>
This design greatly simplifies the database organisation, as the internal
structure does not need to care about layer index translations and all information
needed to build lookup trees is available in a single layout object. It is easy
to temporarily detach a layout object from the libraries without destroying the
structure.
</li>
</ul>
<p>
The design also implies that libraries are read-only. You edit the working
copy only - library cells pulled into the layout are copies that cannot and
should not be edited, as they are restored from their original source
occasionally. Libraries should be thought of as external deliveries similar
to Python modules for example.
</p>
<p>
When saving a working layout, the copies are stored in the layout file too.
This feature can be disabled in a per-library basis in the library declaration
files (only available for static libraries). See the description of the
"replicate" attribute below. Note that when you use this feature, you can
only restore the entire layout, if you have these libraries installed.
</p>
<h2>Library declaration files</h2>
<p>
By default, library declaration files are called "klayout.lib" and are looked up in
all places of the KLayout search path - this includes the home folder, the technology
folders and the package folders. You can override this mechanism by setting the
"KLAYOUT_LIB" environment variable to a specific file. In that case, only this
file is loaded.
</p>
<p>
The format of the library declaration file is "KLayout expressions". This means: function
calls need to be with round brackets and expressions are separated
by ";". For more information about expressions, see <link href="/about/expressions.xml"/>.
</p>
<p>
Two functions are available: "define" and "include". These are used to build
the declaration files.
</p>
<h3>"include" function</h3>
<p><b>Usage:</b></p>
<pre>include(path);</pre>
<p><b>Description:</b></p>
<p>
Includes the lib file given by the path. The path is
resolved relative to the current lib file.
</p>
<h3>"define" function</h3>
<p><b>Usage:</b></p>
<pre>define(name, path [, options]);
define(path [, options]);</pre>
<p><b>Description:</b></p>
<p>
Defines a library. In the path-only version, the library name
is taken from the layout file's LIBNAME record (GDS) or the
file name. In the name/path version, "name" is the name the
library is registered under.
</p>
<p>
The path is resolved relative to the current lib file.
If you want to include an environment variable, use the "env" expression
function:
</p>
<pre>define("A", env("HOME") + "/mylibs/a.gds");</pre>
<p>
Note that the order of library definitions matters. Dependencies
need to be defined already when a library is loaded. For example
if a library "B" uses components from library "A", the order needs
to be:
</p>
<pre>define("A", "a.gds");
define("B", "b.gds");</pre>
<p>
The following options are available:
</p>
<ul>
<li>
<p><b>technology</b>: binds a library to a technology - i.e. it is only
available in the editor when that technology is selected:</p>
<pre>define("A", "a.gds", technology="SG13A");</pre>
<p>
By default, libraries are not bound to a technology, except if
the library file is loaded in the context of a technology (i.e.
sits in a technology folder). You can explicitly enable a library
for all technologies using:
</p>
<pre>define("A", "a.gds", technology="*");</pre>
</li>
<li>
<p><b>technologies</b>: binds a library to several technologies. The
argument is a list:</p>
<pre>define("A", "a.gds", technologies=["SG13A","GF180"]);</pre>
</li>
<li>
<p><b>replicate</b>: this flag indicates that components of the library
are replicated in the layout files using a library element.
This allows loading that file without actually having the library.
However, it comes with a size overhead, that can be avoided
by skipping this replication:
</p>
<pre>define("A", "a.gds", replicate=false);</pre>
<p>By default, replication is enabled.</p>
</li>
<li>
<p><b>description</b>: Specifies a description for that library.
</p>
<pre>define("A", "a.gds", description="Library A");</pre>
</li>
</ul>
</doc> </doc>

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>611</width> <width>619</width>
<height>271</height> <height>475</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -154,6 +154,32 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Synchronize Library Files</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>If this option is enabled, library files will be reloaded automatically. If disabled, you can reload the files using &quot;File/Refresh Libraries&quot; manually.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="auto_sync_libraries">
<property name="text">
<string>Synchronize library files automatically</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>

View File

@ -63,6 +63,7 @@ static const std::string cfg_micron_digits ("digits-micron");
static const std::string cfg_dbu_digits ("digits-dbu"); static const std::string cfg_dbu_digits ("digits-dbu");
static const std::string cfg_assistant_bookmarks ("assistant-bookmarks"); static const std::string cfg_assistant_bookmarks ("assistant-bookmarks");
static const std::string cfg_always_exit_without_saving ("always-exit-without-saving"); static const std::string cfg_always_exit_without_saving ("always-exit-without-saving");
static const std::string cfg_auto_sync_libraries ("auto-sync-libraries");
} }

View File

@ -25,15 +25,14 @@
#include "layApplication.h" #include "layApplication.h"
#include "laySaltController.h" #include "laySaltController.h"
#include "layConfig.h" #include "layConfig.h"
#include "layMainWindow.h"
#include "layQtTools.h" #include "layQtTools.h"
#include "dbLibraryManager.h" #include "dbLibraryManager.h"
#include "dbLibrary.h" #include "dbLibrary.h"
#include "dbReader.h" #include "dbFileBasedLibrary.h"
#include "dbCellMapping.h"
#include "tlLog.h" #include "tlLog.h"
#include "tlStream.h" #include "tlStream.h"
#include "tlFileUtils.h" #include "tlFileUtils.h"
#include "tlEnv.h"
#include <QDir> #include <QDir>
@ -42,84 +41,10 @@ namespace lay
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
class FileBasedLibrary
: public db::Library
{
public:
FileBasedLibrary (const std::string &path)
: db::Library (), m_path (path)
{
set_description (tl::filename (path));
}
void merge_with_other_layout (const std::string &path)
{
m_other_paths.push_back (path);
merge_impl (path);
}
virtual std::string reload ()
{
std::string name = tl::basename (m_path);
layout ().clear ();
tl::InputStream stream (m_path);
db::Reader reader (stream);
reader.read (layout ());
// Use the libname if there is one
db::Layout::meta_info_name_id_type libname_name_id = layout ().meta_info_name_id ("libname");
for (db::Layout::meta_info_iterator m = layout ().begin_meta (); m != layout ().end_meta (); ++m) {
if (m->first == libname_name_id && ! m->second.value.is_nil ()) {
name = m->second.value.to_string ();
break;
}
}
for (auto p = m_other_paths.begin (); p != m_other_paths.end (); ++p) {
merge_impl (*p);
}
return name;
}
private:
std::string m_path;
std::list<std::string> m_other_paths;
void merge_impl (const std::string &path)
{
db::Layout ly;
tl::InputStream stream (path);
db::Reader reader (stream);
reader.read (ly);
std::vector<db::cell_index_type> target_cells, source_cells;
// collect the cells to pull in (all top cells of the library layout)
// NOTE: cells are not overwritten - the first layout wins, in terms
// of cell names and also in terms of database unit.
for (auto c = ly.begin_top_down (); c != ly.end_top_cells (); ++c) {
std::string cn = ly.cell_name (*c);
if (! layout ().has_cell (cn.c_str ())) {
source_cells.push_back (*c);
target_cells.push_back (layout ().add_cell (cn.c_str ()));
}
}
db::CellMapping cm;
cm.create_multi_mapping_full (layout (), target_cells, ly, source_cells);
layout ().copy_tree_shapes (ly, cm);
}
};
// -------------------------------------------------------------------------------------------
LibraryController::LibraryController () LibraryController::LibraryController ()
: m_file_watcher (0), : m_file_watcher (0),
dm_sync_files (this, &LibraryController::sync_files) dm_sync_files (this, &LibraryController::sync_files_maybe),
m_sync (false), m_sync_once (false)
{ {
} }
@ -128,7 +53,7 @@ LibraryController::initialize (lay::Dispatcher * /*root*/)
{ {
// NOTE: we initialize the libraries in the stage once to have them available for the autorun // NOTE: we initialize the libraries in the stage once to have them available for the autorun
// macros. We'll do that later again in order to pull in the libraries from the packages. // macros. We'll do that later again in order to pull in the libraries from the packages.
sync_files (); sync_files (false);
} }
void void
@ -144,7 +69,7 @@ LibraryController::initialized (lay::Dispatcher * /*root*/)
connect (m_file_watcher, SIGNAL (fileRemoved (const QString &)), this, SLOT (file_watcher_triggered ())); connect (m_file_watcher, SIGNAL (fileRemoved (const QString &)), this, SLOT (file_watcher_triggered ()));
} }
sync_files (); sync_files (false);
} }
void void
@ -163,9 +88,9 @@ LibraryController::uninitialize (lay::Dispatcher * /*root*/)
} }
void void
LibraryController::get_options (std::vector < std::pair<std::string, std::string> > & /*options*/) const LibraryController::get_options (std::vector < std::pair<std::string, std::string> > &options) const
{ {
// .. nothing yet .. options.push_back (std::pair<std::string, std::string> (cfg_auto_sync_libraries, "false"));
} }
void void
@ -175,8 +100,21 @@ LibraryController::get_menu_entries (std::vector<lay::MenuEntry> & /*menu_entrie
} }
bool bool
LibraryController::configure (const std::string & /*name*/, const std::string & /*value*/) LibraryController::configure (const std::string &name, const std::string &value)
{ {
if (name == cfg_auto_sync_libraries) {
bool sync = false;
tl::from_string (value, sync);
m_sync = sync;
if (sync) {
dm_sync_files ();
}
}
// don't consume
return false; return false;
} }
@ -194,8 +132,24 @@ LibraryController::can_exit (lay::Dispatcher * /*root*/) const
} }
void void
LibraryController::sync_files () LibraryController::sync_files_maybe ()
{ {
sync_files (false);
}
void
LibraryController::sync_files (bool always)
{
if (tl::verbosity () >= 20) {
if (always) {
tl::info << tl::to_string (tr ("Synchronize library files unconditionally"));
} else {
tl::info << tl::to_string (tr ("Synchronize library files"));
}
}
m_sync_once = false;
if (m_file_watcher) { if (m_file_watcher) {
m_file_watcher->clear (); m_file_watcher->clear ();
m_file_watcher->enable (false); m_file_watcher->enable (false);
@ -228,6 +182,45 @@ LibraryController::sync_files ()
} }
} }
// scan for library definition files
std::string lib_file = tl::get_env ("KLAYOUT_LIB");
std::vector <std::pair<std::string, std::string> > lib_files;
if (lib_file.empty ()) {
for (std::vector <std::pair<std::string, std::string> >::const_iterator p = paths.begin (); p != paths.end (); ++p) {
std::string lf = tl::combine_path (p->first, "klayout.lib");
if (tl::is_readable (lf)) {
lib_files.push_back (std::make_pair (lf, p->second));
}
}
} else if (tl::is_readable (lib_file)) {
lib_files.push_back (std::make_pair (lib_file, std::string ()));
}
for (auto lf = lib_files.begin (); lf != lib_files.end (); ++lf) {
tl::log << "Reading lib file '" << lf->first << "'";
try {
std::vector<LibFileInfo> libs;
read_lib_file (lf->first, lf->second, libs);
read_libs (libs, new_lib_files, always);
if (m_file_watcher) {
m_file_watcher->add_file (tl::absolute_file_path (lf->first));
}
} catch (tl::Exception &ex) {
tl::error << tl::to_string (tr ("Error reading lib file")) << " " << lf->first << ":" << tl::endl << ex.msg ();
} catch (...) {
}
}
// scan for libraries // scan for libraries
for (std::vector <std::pair<std::string, std::string> >::const_iterator p = paths.begin (); p != paths.end (); ++p) { for (std::vector <std::pair<std::string, std::string> >::const_iterator p = paths.begin (); p != paths.end (); ++p) {
@ -245,19 +238,234 @@ LibraryController::sync_files ()
name_filters << QString::fromUtf8 ("*"); name_filters << QString::fromUtf8 ("*");
// NOTE: this should return a list sorted by name // NOTE: this should return a list sorted by name
QStringList libs = lp.entryList (name_filters, QDir::Files); QStringList entries = lp.entryList (name_filters, QDir::Files);
bool needs_load = false; std::vector<LibFileInfo> libs;
libs.reserve (entries.size ());
for (QStringList::const_iterator im = libs.begin (); im != libs.end () && ! needs_load; ++im) { for (auto e = entries.begin (); e != entries.end (); ++e) {
libs.push_back (LibFileInfo ());
libs.back ().path = tl::to_string (lp.absoluteFilePath (*e));
if (! p->second.empty ()) {
libs.back ().tech.insert (p->second);
}
}
std::string lib_path = tl::to_string (lp.absoluteFilePath (*im)); read_libs (libs, new_lib_files, always);
}
}
if (m_file_watcher) {
m_file_watcher->enable (true);
}
// remove libraries which are no longer present
std::set<std::string> new_names;
for (std::map<std::string, LibInfo>::const_iterator lf = new_lib_files.begin (); lf != new_lib_files.end (); ++lf) {
new_names.insert (lf->second.name);
}
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 {
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
m_lib_files = new_lib_files;
}
namespace
{
struct LibFileFunctionContext
{
std::string lib_file;
std::string tech;
std::vector<LibraryController::LibFileInfo> *lib_files;
};
static void do_read_lib_file (LibFileFunctionContext &fc);
class DefineFunction
: public tl::EvalFunction
{
public:
DefineFunction (LibFileFunctionContext *fc)
: mp_fc (fc)
{ }
virtual bool supports_keyword_parameters () const { return true; }
virtual void execute (const tl::ExpressionParserContext &context, tl::Variant & /*out*/, const std::vector<tl::Variant> &args, const std::map<std::string, tl::Variant> *kwargs) const
{
if (args.size () < 1 || args.size () > 2) {
throw tl::EvalError (tl::to_string (tr ("'define' function needs one or two arguments (a path or a name and path)")), context);
}
std::string lf, name;
if (args.size () == 1) {
lf = args[0].to_string ();
} else {
name = args[0].to_string ();
lf = args[1].to_string ();
}
LibraryController::LibFileInfo fi;
fi.name = name;
fi.path = tl::is_absolute (lf) ? lf : tl::combine_path (tl::absolute_path (mp_fc->lib_file), lf);
if (! mp_fc->tech.empty ()) {
fi.tech.insert (mp_fc->tech);
}
if (kwargs) {
for (auto k = kwargs->begin (); k != kwargs->end (); ++k) {
static const std::string replicate_key ("replicate");
static const std::string description_key ("description");
static const std::string technology_key ("technology");
static const std::string technologies_key ("technologies");
if (k->first == replicate_key) {
fi.replicate = k->second.to_bool ();
} else if (k->first == description_key) {
fi.description = k->second.to_string ();
} else if (k->first == technology_key) {
fi.tech.clear ();
std::string tn = k->second.to_string ();
if (! tn.empty () && tn != "*") {
fi.tech.insert (tn);
}
} else if (k->first == technologies_key) {
fi.tech.clear ();
if (k->second.is_list ()) {
for (auto t = k->second.begin (); t != k->second.end (); ++t) {
fi.tech.insert (t->to_string ());
}
}
} else {
throw tl::EvalError (tl::sprintf (tl::to_string (tr ("Unknown keyword argument '%s' for 'define' function - the only allowed keyword arguments are 'replicate', 'technology' and 'technologies")), k->first), context);
}
}
}
mp_fc->lib_files->push_back (fi);
}
private:
LibFileFunctionContext *mp_fc;
};
class IncludeFunction
: public tl::EvalFunction
{
public:
IncludeFunction (LibFileFunctionContext *fc)
: mp_fc (fc)
{ }
virtual void execute (const tl::ExpressionParserContext &context, tl::Variant & /*out*/, const std::vector<tl::Variant> &args, const std::map<std::string, tl::Variant> * /*kwargs*/) const
{
if (args.size () != 1) {
throw tl::EvalError (tl::to_string (tr ("'include' function needs exactly one argument (the include file path)")), context);
}
std::string lf = args[0].to_string ();
LibFileFunctionContext fc = *mp_fc;
fc.lib_file = tl::is_absolute (lf) ? lf : tl::combine_path (tl::absolute_path (mp_fc->lib_file), lf);
do_read_lib_file (fc);
}
private:
LibFileFunctionContext *mp_fc;
};
static void
do_read_lib_file (LibFileFunctionContext &fc)
{
tl::Eval eval;
eval.define_function ("define", new DefineFunction (&fc));
eval.define_function ("include", new IncludeFunction (&fc));
eval.set_var ("file", fc.lib_file);
eval.set_var ("tech", fc.tech);
tl::InputStream is (fc.lib_file);
std::string lib_file = tl::TextInputStream (is).read_all ();
tl::Extractor ex (lib_file.c_str ());
eval.parse (ex).execute ();
}
}
void
LibraryController::read_lib_file (const std::string &lib_file, const std::string &tech, std::vector<LibraryController::LibFileInfo> &file_info)
{
LibFileFunctionContext fc;
fc.tech = tech;
fc.lib_file = tl::absolute_file_path (lib_file);
fc.lib_files = &file_info;
do_read_lib_file (fc);
}
void
LibraryController::read_libs (const std::vector<LibraryController::LibFileInfo> &libs, std::map<std::string, LibraryController::LibInfo> &new_lib_files, bool always)
{
bool needs_load = always;
for (auto im = libs.begin (); im != libs.end () && ! needs_load; ++im) {
const std::string &lib_path = im->path;
QFileInfo fi (tl::to_qstring (lib_path)); QFileInfo fi (tl::to_qstring (lib_path));
auto ll = m_lib_files.find (lib_path); auto ll = m_lib_files.find (lib_path);
if (ll == m_lib_files.end ()) { if (ll == m_lib_files.end ()) {
needs_load = true; needs_load = true;
} else if (fi.lastModified () > ll->second.time) { } else if (fi.lastModified () > ll->second.time || im->tech != ll->second.tech || im->replicate != ll->second.replicate) {
needs_load = true; needs_load = true;
} }
@ -267,9 +475,9 @@ LibraryController::sync_files ()
// If not reloading, register the existing files as known ones - this allows detecting if // If not reloading, register the existing files as known ones - this allows detecting if
// a file got removed. // a file got removed.
for (QStringList::const_iterator im = libs.begin (); im != libs.end () && ! needs_load; ++im) { for (auto im = libs.begin (); im != libs.end () && ! needs_load; ++im) {
std::string lib_path = tl::to_string (lp.absoluteFilePath (*im)); const std::string &lib_path = im->path;
auto ll = m_lib_files.find (lib_path); auto ll = m_lib_files.find (lib_path);
if (ll != m_lib_files.end ()) { if (ll != m_lib_files.end ()) {
new_lib_files.insert (*ll); new_lib_files.insert (*ll);
@ -279,23 +487,23 @@ LibraryController::sync_files ()
} else { } else {
std::map<std::string, FileBasedLibrary *> libs_by_name_here; std::map<std::string, db::FileBasedLibrary *> libs_by_name_here;
// Reload all files // Reload all files
for (QStringList::const_iterator im = libs.begin (); im != libs.end (); ++im) { for (auto im = libs.begin (); im != libs.end (); ++im) {
std::string lib_path = tl::to_string (lp.absoluteFilePath (*im)); const std::string &lib_path = im->path;
QFileInfo fi (tl::to_qstring (lib_path)); QFileInfo fi (tl::to_qstring (lib_path));
try { try {
std::unique_ptr<FileBasedLibrary> lib (new FileBasedLibrary (lib_path)); std::unique_ptr<db::FileBasedLibrary> lib (new db::FileBasedLibrary (lib_path, im->name));
if (! p->second.empty ()) { lib->set_technologies (im->tech);
lib->set_technology (p->second); lib->set_description (im->description);
} lib->set_replicate (im->replicate);
tl::log << "Reading library '" << lib_path << "'"; tl::log << "Reading library '" << lib_path << "'";
std::string libname = lib->reload (); std::string libname = lib->load ();
// merge with existing lib if there is already one in this folder with the right name // merge with existing lib if there is already one in this folder with the right name
auto il = libs_by_name_here.find (libname); auto il = libs_by_name_here.find (libname);
@ -311,8 +519,8 @@ LibraryController::sync_files ()
// otherwise register the new library // otherwise register the new library
if (! p->second.empty ()) { if (! im->tech.empty ()) {
tl::log << "Registering as '" << libname << "' for tech '" << p->second << "'"; tl::log << "Registering as '" << libname << "' for tech '" << tl::join (im->tech.begin (), im->tech.end (), ",") << "'";
} else { } else {
tl::log << "Registering as '" << libname << "'"; tl::log << "Registering as '" << libname << "'";
} }
@ -320,9 +528,9 @@ LibraryController::sync_files ()
LibInfo li; LibInfo li;
li.name = libname; li.name = libname;
li.time = fi.lastModified (); li.time = fi.lastModified ();
if (! p->second.empty ()) { li.description = im->description;
li.tech.insert (p->second); li.tech = im->tech;
} li.replicate = im->replicate;
new_lib_files.insert (std::make_pair (lib_path, li)); new_lib_files.insert (std::make_pair (lib_path, li));
lib->set_name (libname); lib->set_name (libname);
@ -347,59 +555,23 @@ LibraryController::sync_files ()
} }
} }
}
}
if (m_file_watcher) {
m_file_watcher->enable (true);
}
// remove libraries which are no longer present
std::set<std::string> new_names;
for (std::map<std::string, LibInfo>::const_iterator lf = new_lib_files.begin (); lf != new_lib_files.end (); ++lf) {
new_names.insert (lf->second.name);
}
for (std::map<std::string, LibInfo>::const_iterator lf = m_lib_files.begin (); lf != m_lib_files.end (); ++lf) {
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));
}
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
} catch (...) {
}
}
}
// establish the new libraries
m_lib_files = new_lib_files;
} }
void void
LibraryController::sync_with_external_sources () LibraryController::sync_with_external_sources ()
{ {
tl::log << tl::to_string (tr ("Package updates - updating libraries")); tl::log << tl::to_string (tr ("Package updates - updating libraries"));
m_sync_once = true;
dm_sync_files (); dm_sync_files ();
} }
void void
LibraryController::file_watcher_triggered () LibraryController::file_watcher_triggered ()
{ {
if (m_sync) {
tl::log << tl::to_string (tr ("Detected file system change in libraries - updating")); tl::log << tl::to_string (tr ("Detected file system change in libraries - updating"));
dm_sync_files (); dm_sync_files ();
}
} }
LibraryController * LibraryController *

View File

@ -51,12 +51,22 @@ class MainWindow;
* By making the controller a PluginDeclaration it will receive * By making the controller a PluginDeclaration it will receive
* initialization and configuration calls. * initialization and configuration calls.
*/ */
class LibraryController class LAY_PUBLIC LibraryController
: public lay::PluginDeclaration, public tl::Object : public lay::PluginDeclaration, public tl::Object
{ {
Q_OBJECT Q_OBJECT
public: public:
struct LibFileInfo
{
LibFileInfo () : name (), path (), replicate (true) { }
std::string name;
std::string description;
std::string path;
std::set<std::string> tech;
bool replicate;
};
/** /**
* @brief Default constructor * @brief Default constructor
*/ */
@ -102,11 +112,23 @@ public:
*/ */
bool can_exit (lay::Dispatcher *root) const; bool can_exit (lay::Dispatcher *root) const;
/**
* @brief Synchronize files
*
* If "always" is set, all files are synchronized unconditionally.
*/
void sync_files (bool always);
/** /**
* @brief Gets the singleton instance for this object * @brief Gets the singleton instance for this object
*/ */
static LibraryController *instance (); static LibraryController *instance ();
/**
* @brief Provided for test purposes
*/
static void read_lib_file (const std::string &lib_file, const std::string &tech, std::vector<LibFileInfo> &file_info);
private slots: private slots:
/** /**
* @brief Called when the file watcher detects a change in the file system * @brief Called when the file watcher detects a change in the file system
@ -121,17 +143,22 @@ private slots:
private: private:
struct LibInfo struct LibInfo
{ {
LibInfo () : name (), time (), tech () { } LibInfo () : name (), description (), time (), tech (), replicate (true) { }
std::string name; std::string name;
std::string description;
QDateTime time; QDateTime time;
std::set<std::string> tech; std::set<std::string> tech;
bool replicate;
}; };
tl::FileSystemWatcher *m_file_watcher; tl::FileSystemWatcher *m_file_watcher;
tl::DeferredMethod<LibraryController> dm_sync_files; tl::DeferredMethod<LibraryController> dm_sync_files;
std::map<std::string, LibInfo> m_lib_files; std::map<std::string, LibInfo> m_lib_files;
bool m_sync;
bool m_sync_once;
void sync_files (); void sync_files_maybe ();
void read_libs (const std::vector<LibFileInfo> &file_info, std::map<std::string, LibInfo> &new_lib_files, bool always);
}; };
} }

View File

@ -197,6 +197,10 @@ MainConfigPage7::setup (lay::Dispatcher *root)
root->config_get (cfg_layout_file_watcher_enabled, en); root->config_get (cfg_layout_file_watcher_enabled, en);
mp_ui->check_for_updates->setChecked (en); mp_ui->check_for_updates->setChecked (en);
bool asl = false;
root->config_get (cfg_auto_sync_libraries, asl);
mp_ui->auto_sync_libraries->setChecked (asl);
int kb = 0; int kb = 0;
root->config_get (cfg_keep_backups, kb); root->config_get (cfg_keep_backups, kb);
mp_ui->keep_backups->setValue (kb); mp_ui->keep_backups->setValue (kb);
@ -211,6 +215,7 @@ MainConfigPage7::commit (lay::Dispatcher *root)
{ {
try { try {
root->config_set (cfg_layout_file_watcher_enabled, mp_ui->check_for_updates->isChecked ()); root->config_set (cfg_layout_file_watcher_enabled, mp_ui->check_for_updates->isChecked ());
root->config_set (cfg_auto_sync_libraries, mp_ui->auto_sync_libraries->isChecked ());
root->config_set (cfg_keep_backups, mp_ui->keep_backups->value ()); root->config_set (cfg_keep_backups, mp_ui->keep_backups->value ());
root->config_set (cfg_always_exit_without_saving, mp_ui->always_exit_without_saving->isChecked ()); root->config_set (cfg_always_exit_without_saving, mp_ui->always_exit_without_saving->isChecked ());
} catch (...) { } } catch (...) { }

View File

@ -94,6 +94,7 @@
#include "laySettingsForm.h" #include "laySettingsForm.h"
#include "laySelectCellViewForm.h" #include "laySelectCellViewForm.h"
#include "layTechnologyController.h" #include "layTechnologyController.h"
#include "layLibraryController.h"
#include "laySaltController.h" #include "laySaltController.h"
#include "layTipDialog.h" #include "layTipDialog.h"
#include "layMacroController.h" #include "layMacroController.h"
@ -2612,6 +2613,10 @@ MainWindow::cm_writer_options ()
void void
MainWindow::cm_refresh () MainWindow::cm_refresh ()
{ {
if (lay::LibraryController::instance ()) {
lay::LibraryController::instance ()->sync_files (false);
}
db::LibraryManager::instance ().refresh_all (); db::LibraryManager::instance ().refresh_all ();
} }

View File

@ -0,0 +1,78 @@
/*
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
*/
#include "layLibraryController.h"
#include "tlUnitTest.h"
#include "tlFileUtils.h"
TEST (1)
{
std::string lib_file = tl::testdata () + "/lay/a.libdef";
std::vector<lay::LibraryController::LibFileInfo> file_info;
lay::LibraryController::read_lib_file (lib_file, "T1", file_info);
tl_assert (file_info.size () == size_t (3));
EXPECT_EQ (file_info[0].name, "");
EXPECT_EQ (file_info[0].path, tl::combine_path (tl::absolute_path (lib_file), "noname.gds"));
EXPECT_EQ (file_info[0].replicate, true);
EXPECT_EQ (file_info[0].description, "");
EXPECT_EQ (tl::join (file_info[0].tech.begin (), file_info[0].tech.end (), ","), "T1");
EXPECT_EQ (file_info[1].name, "L2");
EXPECT_EQ (file_info[1].path, tl::absolute_file_path (lib_file) + ".zzz");
EXPECT_EQ (file_info[1].replicate, true);
EXPECT_EQ (file_info[1].description, "Library L2");
EXPECT_EQ (file_info[1].tech.size (), size_t (0));
EXPECT_EQ (file_info[2].name, "L3");
EXPECT_EQ (file_info[2].path, tl::combine_path (tl::absolute_path (lib_file), "subdir/l3.gds"));
EXPECT_EQ (file_info[2].replicate, false);
EXPECT_EQ (tl::join (file_info[2].tech.begin (), file_info[2].tech.end (), ","), "T2,T3");
}
TEST(2)
{
std::string lib_file = tl::testdata () + "/lay/b.libdef";
std::vector<lay::LibraryController::LibFileInfo> file_info;
lay::LibraryController::read_lib_file (lib_file, "TX", file_info);
tl_assert (file_info.size () == size_t (5));
EXPECT_EQ (file_info[0].name, "L0");
EXPECT_EQ (file_info[0].path, tl::combine_path (tl::absolute_path (lib_file), "l0.gds"));
EXPECT_EQ (file_info[0].replicate, true);
EXPECT_EQ (file_info[0].tech.size (), size_t (0));
EXPECT_EQ (file_info[1].name, "");
EXPECT_EQ (file_info[1].path, tl::combine_path (tl::absolute_path (lib_file), "noname.gds"));
EXPECT_EQ (file_info[1].replicate, true);
EXPECT_EQ (tl::join (file_info[1].tech.begin (), file_info[1].tech.end (), ","), "TX");
EXPECT_EQ (file_info[4].name, "L4");
EXPECT_EQ (file_info[4].path, tl::combine_path (tl::absolute_path (lib_file), "l4.gds"));
EXPECT_EQ (file_info[4].replicate, true);
EXPECT_EQ (tl::join (file_info[4].tech.begin (), file_info[4].tech.end (), ","), "TX");
}

View File

@ -7,6 +7,7 @@ TARGET = lay_tests
include($$PWD/../../lib_ut.pri) include($$PWD/../../lib_ut.pri)
SOURCES = \ SOURCES = \
layLibraryControllerTests.cc \
laySalt.cc \ laySalt.cc \
layHelpIndexTest.cc \ layHelpIndexTest.cc \
laySaltParsedURLTests.cc \ laySaltParsedURLTests.cc \

View File

@ -601,6 +601,27 @@ LayoutViewFunctions::cm_cell_convert_to_static ()
db::Layout &layout = view ()->cellview (cv_index)->layout (); db::Layout &layout = view ()->cellview (cv_index)->layout ();
// check that all (unselected) parents are static cells - must not convert children
// of proxy cells.
std::set<db::cell_index_type> to_convert;
for (std::vector<lay::LayoutViewBase::cell_path_type>::iterator p = paths.begin (); p != paths.end (); ++p) {
if (! p->empty () && layout.is_valid_cell_index (p->back ())) {
to_convert.insert (p->back ());
}
}
for (auto c = to_convert.begin (); c != to_convert.end (); ++c) {
std::set<db::cell_index_type> callers;
layout.cell (*c).collect_caller_cells (callers);
for (auto cc = callers.begin (); cc != callers.end (); ++cc) {
if (layout.cell (*cc).is_proxy () && to_convert.find (*cc) == to_convert.end ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot convert cells to static cells if they have library or PCell parents - here: %s as child of %s")),
layout.cell (*c).get_display_name (), layout.cell (*cc).get_display_name ()));
}
}
}
// remember the current path // remember the current path
lay::LayoutViewBase::cell_path_type cell_path (view ()->cellview (cv_index).combined_unspecific_path ()); lay::LayoutViewBase::cell_path_type cell_path (view ()->cellview (cv_index).combined_unspecific_path ());
@ -610,6 +631,8 @@ LayoutViewFunctions::cm_cell_convert_to_static ()
std::map<db::cell_index_type, db::cell_index_type> cell_map; std::map<db::cell_index_type, db::cell_index_type> cell_map;
// perform the conversion
for (std::vector<lay::LayoutViewBase::cell_path_type>::iterator p = paths.begin (); p != paths.end (); ++p) { for (std::vector<lay::LayoutViewBase::cell_path_type>::iterator p = paths.begin (); p != paths.end (); ++p) {
if (! p->empty () && layout.is_valid_cell_index (p->back ())) { if (! p->empty () && layout.is_valid_cell_index (p->back ())) {
db::cell_index_type new_cell = layout.convert_cell_to_static (p->back ()); db::cell_index_type new_cell = layout.convert_cell_to_static (p->back ());

View File

@ -52,56 +52,56 @@ TEST(1_Circle)
params["actual_radius"] = 10.0; params["actual_radius"] = 10.0;
db::cell_index_type lib_cell; db::cell_index_type lib_cell;
db::Cell *circle; db::cell_index_type circle;
std::vector<tl::Variant> plist; std::vector<tl::Variant> plist;
lib_cell = lib_basic->layout ().get_pcell_variant_dict (pc.second, params); lib_cell = lib_basic->layout ().get_pcell_variant_dict (pc.second, params);
circle = &ly.cell (ly.get_lib_proxy (lib_basic, lib_cell)); circle = ly.get_lib_proxy (lib_basic, lib_cell);
// change radius explicitly // change radius explicitly
// has radius 10um // has radius 10um
EXPECT_EQ (circle->bbox ().to_string (), "(-10000,-10000;10000,10000)"); EXPECT_EQ (ly.cell (circle).bbox ().to_string (), "(-10000,-10000;10000,10000)");
EXPECT_EQ (circle->get_display_name (), "Basic.CIRCLE(l=1/0,r=10,n=64)"); EXPECT_EQ (ly.cell (circle).get_display_name (), "Basic.CIRCLE(l=1/0,r=10,n=64)");
// only after Library::refresh the parameters get updated // only after Library::refresh the parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (circle->cell_index ()) [p_radius].to_double (), 0.0); EXPECT_EQ (ly.get_pcell_parameters (circle) [p_radius].to_double (), 0.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (circle->bbox ().to_string (), "(-10000,-10000;10000,10000)"); EXPECT_EQ (ly.cell (circle).bbox ().to_string (), "(-10000,-10000;10000,10000)");
EXPECT_EQ (circle->get_display_name (), "Basic.CIRCLE(l=1/0,r=10,n=64)"); EXPECT_EQ (ly.cell (circle).get_display_name (), "Basic.CIRCLE(l=1/0,r=10,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (circle->cell_index ()) [p_radius].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (circle) [p_radius].to_double (), 10.0);
EXPECT_EQ (ly.get_pcell_parameters (circle->cell_index ()) [p_actual_radius].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (circle) [p_actual_radius].to_double (), 10.0);
// change radius explicitly // change radius explicitly
plist = ly.get_pcell_parameters (circle->cell_index ()); plist = ly.get_pcell_parameters (ly.cell (circle).cell_index ());
plist[p_actual_radius] = 9.0; plist[p_actual_radius] = 9.0;
circle = &ly.cell (ly.get_pcell_variant_cell (circle->cell_index (), plist)); circle = ly.get_pcell_variant_cell (circle, plist);
// as the radius is an input parameter, only after Library::refresh the other parameters get updated // as the radius is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (circle->cell_index ()) [p_radius].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (circle) [p_radius].to_double (), 10.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (circle->bbox ().to_string (), "(-9000,-9000;9000,9000)"); EXPECT_EQ (ly.cell (circle).bbox ().to_string (), "(-9000,-9000;9000,9000)");
EXPECT_EQ (circle->get_display_name (), "Basic.CIRCLE(l=1/0,r=9,n=64)"); EXPECT_EQ (ly.cell (circle).get_display_name (), "Basic.CIRCLE(l=1/0,r=9,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (circle->cell_index ()) [p_radius].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (circle) [p_radius].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (circle->cell_index ()) [p_actual_radius].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (circle) [p_actual_radius].to_double (), 9.0);
// change handle explicitly // change handle explicitly
plist = ly.get_pcell_parameters (circle->cell_index ()); plist = ly.get_pcell_parameters (circle);
plist[p_handle] = db::DPoint (0.0, 8.0); plist[p_handle] = db::DPoint (0.0, 8.0);
circle = &ly.cell (ly.get_pcell_variant_cell (circle->cell_index (), plist)); circle = ly.get_pcell_variant_cell (circle, plist);
// as the handle is an input parameter, only after Library::refresh the other parameters get updated // as the handle is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (circle->cell_index ()) [p_actual_radius].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (circle) [p_actual_radius].to_double (), 9.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (circle->bbox ().to_string (), "(-8000,-8000;8000,8000)"); EXPECT_EQ (ly.cell (circle).bbox ().to_string (), "(-8000,-8000;8000,8000)");
EXPECT_EQ (circle->get_display_name (), "Basic.CIRCLE(l=1/0,r=8,n=64)"); EXPECT_EQ (ly.cell (circle).get_display_name (), "Basic.CIRCLE(l=1/0,r=8,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (circle->cell_index ()) [p_radius].to_double (), 8.0); EXPECT_EQ (ly.get_pcell_parameters (circle) [p_radius].to_double (), 8.0);
EXPECT_EQ (ly.get_pcell_parameters (circle->cell_index ()) [p_actual_radius].to_double (), 8.0); EXPECT_EQ (ly.get_pcell_parameters (circle) [p_actual_radius].to_double (), 8.0);
unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0));
@ -145,145 +145,145 @@ TEST(2_Pie)
params["actual_end_angle"] = 0.0; params["actual_end_angle"] = 0.0;
db::cell_index_type lib_cell; db::cell_index_type lib_cell;
db::Cell *pie; db::cell_index_type pie;
std::vector<tl::Variant> plist; std::vector<tl::Variant> plist;
lib_cell = lib_basic->layout ().get_pcell_variant_dict (pc.second, params); lib_cell = lib_basic->layout ().get_pcell_variant_dict (pc.second, params);
pie = &ly.cell (ly.get_lib_proxy (lib_basic, lib_cell)); pie = ly.get_lib_proxy (lib_basic, lib_cell);
// change radius explicitly // change radius explicitly
// has radius 10um, but bbox isn't correct for now (because handle was not updated) // has radius 10um, but bbox isn't correct for now (because handle was not updated)
EXPECT_EQ (pie->bbox ().to_string (), "(-1000,-10000;10000,1000)"); EXPECT_EQ (ly.cell (pie).bbox ().to_string (), "(-1000,-10000;10000,1000)");
EXPECT_EQ (pie->get_display_name (), "Basic.PIE(l=1/0,r=10,a=-90..0,n=64)"); EXPECT_EQ (ly.cell (pie).get_display_name (), "Basic.PIE(l=1/0,r=10,a=-90..0,n=64)");
// only after Library::refresh the parameters get updated and bbox is correct // only after Library::refresh the parameters get updated and bbox is correct
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_radius].to_double (), 0.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_radius].to_double (), 0.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (pie->bbox ().to_string (), "(0,-10000;10000,0)"); EXPECT_EQ (ly.cell (pie).bbox ().to_string (), "(0,-10000;10000,0)");
EXPECT_EQ (pie->get_display_name (), "Basic.PIE(l=1/0,r=10,a=-90..0,n=64)"); EXPECT_EQ (ly.cell (pie).get_display_name (), "Basic.PIE(l=1/0,r=10,a=-90..0,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_radius].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_radius].to_double (), 10.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_radius].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_radius].to_double (), 10.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_end_angle].to_double (), 0);
// change radius explicitly // change radius explicitly
plist = ly.get_pcell_parameters (pie->cell_index ()); plist = ly.get_pcell_parameters (pie);
plist[p_actual_radius] = 9.0; plist[p_actual_radius] = 9.0;
pie = &ly.cell (ly.get_pcell_variant_cell (pie->cell_index (), plist)); pie = ly.get_pcell_variant_cell (pie, plist);
// as the radius is an input parameter, only after Library::refresh the other parameters get updated // as the radius is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_radius].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_radius].to_double (), 10.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (pie->bbox ().to_string (), "(0,-9000;9000,0)"); EXPECT_EQ (ly.cell (pie).bbox ().to_string (), "(0,-9000;9000,0)");
EXPECT_EQ (pie->get_display_name (), "Basic.PIE(l=1/0,r=9,a=-90..0,n=64)"); EXPECT_EQ (ly.cell (pie).get_display_name (), "Basic.PIE(l=1/0,r=9,a=-90..0,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_handle1].to_string (), "0,-9"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle1].to_string (), "0,-9");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_handle2].to_string (), "9,0"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_handle1].to_string (), "0,-9"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle1].to_string (), "0,-9");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_handle2].to_string (), "9,0"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_radius].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_radius].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_radius].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_radius].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_end_angle].to_double (), 0);
// change end angle explicitly // change end angle explicitly
plist = ly.get_pcell_parameters (pie->cell_index ()); plist = ly.get_pcell_parameters (pie);
plist[p_actual_end_angle] = 90.0; plist[p_actual_end_angle] = 90.0;
pie = &ly.cell (ly.get_pcell_variant_cell (pie->cell_index (), plist)); pie = ly.get_pcell_variant_cell (pie, plist);
// as the end angle is an input parameter, only after Library::refresh the other parameters get updated // as the end angle is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_end_angle].to_double (), 0.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_end_angle].to_double (), 0.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (pie->bbox ().to_string (), "(0,-9000;9000,9000)"); EXPECT_EQ (ly.cell (pie).bbox ().to_string (), "(0,-9000;9000,9000)");
EXPECT_EQ (pie->get_display_name (), "Basic.PIE(l=1/0,r=9,a=-90..90,n=64)"); EXPECT_EQ (ly.cell (pie).get_display_name (), "Basic.PIE(l=1/0,r=9,a=-90..90,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_handle1].to_string (), "0,-9"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle1].to_string (), "0,-9");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_handle1].to_string (), "0,-9"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle1].to_string (), "0,-9");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_radius].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_radius].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_radius].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_radius].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_end_angle].to_double (), 90);
// change start angle explicitly // change start angle explicitly
plist = ly.get_pcell_parameters (pie->cell_index ()); plist = ly.get_pcell_parameters (pie);
plist[p_actual_start_angle] = 0.0; plist[p_actual_start_angle] = 0.0;
pie = &ly.cell (ly.get_pcell_variant_cell (pie->cell_index (), plist)); pie = ly.get_pcell_variant_cell (pie, plist);
// as the end angle is an input parameter, only after Library::refresh the other parameters get updated // as the end angle is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_start_angle].to_double (), -90.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_start_angle].to_double (), -90.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (pie->bbox ().to_string (), "(0,0;9000,9000)"); EXPECT_EQ (ly.cell (pie).bbox ().to_string (), "(0,0;9000,9000)");
EXPECT_EQ (pie->get_display_name (), "Basic.PIE(l=1/0,r=9,a=0..90,n=64)"); EXPECT_EQ (ly.cell (pie).get_display_name (), "Basic.PIE(l=1/0,r=9,a=0..90,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_handle1].to_string (), "9,0"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle1].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_handle1].to_string (), "9,0"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle1].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_radius].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_radius].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_radius].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_radius].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_start_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_start_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_start_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_start_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_end_angle].to_double (), 90);
// change handle1 explicitly // change handle1 explicitly
plist = ly.get_pcell_parameters (pie->cell_index ()); plist = ly.get_pcell_parameters (pie);
plist[p_actual_handle1] = db::DPoint (0.0, -5.0); plist[p_actual_handle1] = db::DPoint (0.0, -5.0);
pie = &ly.cell (ly.get_pcell_variant_cell (pie->cell_index (), plist)); pie = ly.get_pcell_variant_cell (pie, plist);
// as the handle is an input parameter, only after Library::refresh the other parameters get updated // as the handle is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_start_angle].to_double (), 0.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_start_angle].to_double (), 0.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (pie->bbox ().to_string (), "(0,-9000;9000,9000)"); EXPECT_EQ (ly.cell (pie).bbox ().to_string (), "(0,-9000;9000,9000)");
EXPECT_EQ (pie->get_display_name (), "Basic.PIE(l=1/0,r=9,a=-90..90,n=64)"); EXPECT_EQ (ly.cell (pie).get_display_name (), "Basic.PIE(l=1/0,r=9,a=-90..90,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_start_angle].to_double (), -90.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_start_angle].to_double (), -90.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_start_angle].to_double (), -90.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_start_angle].to_double (), -90.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_radius].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_radius].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_radius].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_radius].to_double (), 9);
// change handle2 explicitly // change handle2 explicitly
plist = ly.get_pcell_parameters (pie->cell_index ()); plist = ly.get_pcell_parameters (pie);
plist[p_actual_handle2] = db::DPoint (5.0, 0.0); plist[p_actual_handle2] = db::DPoint (5.0, 0.0);
pie = &ly.cell (ly.get_pcell_variant_cell (pie->cell_index (), plist)); pie = ly.get_pcell_variant_cell (pie, plist);
// as the handle is an input parameter, only after Library::refresh the other parameters get updated // as the handle is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_end_angle].to_double (), 90.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_end_angle].to_double (), 90.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (pie->bbox ().to_string (), "(0,-5000;5000,0)"); EXPECT_EQ (ly.cell (pie).bbox ().to_string (), "(0,-5000;5000,0)");
EXPECT_EQ (pie->get_display_name (), "Basic.PIE(l=1/0,r=5,a=-90..0,n=64)"); EXPECT_EQ (ly.cell (pie).get_display_name (), "Basic.PIE(l=1/0,r=5,a=-90..0,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_handle2].to_string (), "5,0"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle2].to_string (), "5,0");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_handle2].to_string (), "5,0"); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle2].to_string (), "5,0");
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_start_angle].to_double (), -90.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_start_angle].to_double (), -90.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_start_angle].to_double (), -90.0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_start_angle].to_double (), -90.0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_radius].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_radius].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_radius].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_radius].to_double (), 5);
} }
TEST(3_Arc) TEST(3_Arc)
@ -320,182 +320,182 @@ TEST(3_Arc)
params["actual_end_angle"] = 0.0; params["actual_end_angle"] = 0.0;
db::cell_index_type lib_cell; db::cell_index_type lib_cell;
db::Cell *arc; db::cell_index_type arc;
std::vector<tl::Variant> plist; std::vector<tl::Variant> plist;
lib_cell = lib_basic->layout ().get_pcell_variant_dict (pc.second, params); lib_cell = lib_basic->layout ().get_pcell_variant_dict (pc.second, params);
arc = &ly.cell (ly.get_lib_proxy (lib_basic, lib_cell)); arc = ly.get_lib_proxy (lib_basic, lib_cell);
// change radius explicitly // change radius explicitly
// has radius 10um, but bbox isn't correct for now (because handle was not updated) // has radius 10um, but bbox isn't correct for now (because handle was not updated)
EXPECT_EQ (arc->bbox ().to_string (), "(0,-10000;10000,1000)"); EXPECT_EQ (ly.cell (arc).bbox ().to_string (), "(0,-10000;10000,1000)");
EXPECT_EQ (arc->get_display_name (), "Basic.ARC(l=1/0,r=4..10,a=-90..0,n=64)"); EXPECT_EQ (ly.cell (arc).get_display_name (), "Basic.ARC(l=1/0,r=4..10,a=-90..0,n=64)");
// only after Library::refresh the parameters get updated and bbox is correct // only after Library::refresh the parameters get updated and bbox is correct
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius1].to_double (), 0.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius1].to_double (), 0.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (arc->bbox ().to_string (), "(0,-10000;10000,0)"); EXPECT_EQ (ly.cell (arc).bbox ().to_string (), "(0,-10000;10000,0)");
EXPECT_EQ (arc->get_display_name (), "Basic.ARC(l=1/0,r=4..10,a=-90..0,n=64)"); EXPECT_EQ (ly.cell (arc).get_display_name (), "Basic.ARC(l=1/0,r=4..10,a=-90..0,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius1].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius1].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius1].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius1].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius2].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius2].to_double (), 10.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius2].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius2].to_double (), 10.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 0);
// change radius2 explicitly // change radius2 explicitly
plist = ly.get_pcell_parameters (arc->cell_index ()); plist = ly.get_pcell_parameters (arc);
plist[p_actual_radius2] = 9.0; plist[p_actual_radius2] = 9.0;
arc = &ly.cell (ly.get_pcell_variant_cell (arc->cell_index (), plist)); arc = ly.get_pcell_variant_cell (arc, plist);
// as the radius is an input parameter, only after Library::refresh the other parameters get updated // as the radius is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius2].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius2].to_double (), 10.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (arc->bbox ().to_string (), "(0,-9000;9000,0)"); EXPECT_EQ (ly.cell (arc).bbox ().to_string (), "(0,-9000;9000,0)");
EXPECT_EQ (arc->get_display_name (), "Basic.ARC(l=1/0,r=4..9,a=-90..0,n=64)"); EXPECT_EQ (ly.cell (arc).get_display_name (), "Basic.ARC(l=1/0,r=4..9,a=-90..0,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle1].to_string (), "0,-4"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle1].to_string (), "0,-4");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle2].to_string (), "9,0"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle1].to_string (), "0,-4"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "0,-4");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle2].to_string (), "9,0"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius1].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius1].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius1].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius1].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 0);
// change radius1 explicitly // change radius1 explicitly
plist = ly.get_pcell_parameters (arc->cell_index ()); plist = ly.get_pcell_parameters (arc);
plist[p_actual_radius1] = 5.0; plist[p_actual_radius1] = 5.0;
arc = &ly.cell (ly.get_pcell_variant_cell (arc->cell_index (), plist)); arc = ly.get_pcell_variant_cell (arc, plist);
// as the radius is an input parameter, only after Library::refresh the other parameters get updated // as the radius is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius1].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius1].to_double (), 4.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (arc->bbox ().to_string (), "(0,-9000;9000,0)"); EXPECT_EQ (ly.cell (arc).bbox ().to_string (), "(0,-9000;9000,0)");
EXPECT_EQ (arc->get_display_name (), "Basic.ARC(l=1/0,r=5..9,a=-90..0,n=64)"); EXPECT_EQ (ly.cell (arc).get_display_name (), "Basic.ARC(l=1/0,r=5..9,a=-90..0,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle2].to_string (), "9,0"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle2].to_string (), "9,0"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius1].to_double (), 5.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius1].to_double (), 5.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius1].to_double (), 5.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius1].to_double (), 5.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 0);
// change end angle explicitly // change end angle explicitly
plist = ly.get_pcell_parameters (arc->cell_index ()); plist = ly.get_pcell_parameters (arc);
plist[p_actual_end_angle] = 90.0; plist[p_actual_end_angle] = 90.0;
arc = &ly.cell (ly.get_pcell_variant_cell (arc->cell_index (), plist)); arc = ly.get_pcell_variant_cell (arc, plist);
// as the end angle is an input parameter, only after Library::refresh the other parameters get updated // as the end angle is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_end_angle].to_double (), 0.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_end_angle].to_double (), 0.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (arc->bbox ().to_string (), "(0,-9000;9000,9000)"); EXPECT_EQ (ly.cell (arc).bbox ().to_string (), "(0,-9000;9000,9000)");
EXPECT_EQ (arc->get_display_name (), "Basic.ARC(l=1/0,r=5..9,a=-90..90,n=64)"); EXPECT_EQ (ly.cell (arc).get_display_name (), "Basic.ARC(l=1/0,r=5..9,a=-90..90,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius1].to_double (), 5.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius1].to_double (), 5.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius1].to_double (), 5.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius1].to_double (), 5.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_start_angle].to_double (), -90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_start_angle].to_double (), -90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 90);
// change start angle explicitly // change start angle explicitly
plist = ly.get_pcell_parameters (arc->cell_index ()); plist = ly.get_pcell_parameters (arc);
plist[p_actual_start_angle] = 0.0; plist[p_actual_start_angle] = 0.0;
arc = &ly.cell (ly.get_pcell_variant_cell (arc->cell_index (), plist)); arc = ly.get_pcell_variant_cell (arc, plist);
// as the end angle is an input parameter, only after Library::refresh the other parameters get updated // as the end angle is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_start_angle].to_double (), -90.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_start_angle].to_double (), -90.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (arc->bbox ().to_string (), "(0,0;9000,9000)"); EXPECT_EQ (ly.cell (arc).bbox ().to_string (), "(0,0;9000,9000)");
EXPECT_EQ (arc->get_display_name (), "Basic.ARC(l=1/0,r=5..9,a=0..90,n=64)"); EXPECT_EQ (ly.cell (arc).get_display_name (), "Basic.ARC(l=1/0,r=5..9,a=0..90,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle1].to_string (), "5,0"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle1].to_string (), "5,0");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle1].to_string (), "5,0"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "5,0");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius1].to_double (), 5.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius1].to_double (), 5.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius1].to_double (), 5.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius1].to_double (), 5.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_start_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_start_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_start_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_start_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 90);
// change handle1 explicitly // change handle1 explicitly
plist = ly.get_pcell_parameters (arc->cell_index ()); plist = ly.get_pcell_parameters (arc);
plist[p_actual_handle1] = db::DPoint (0.0, -5.0); plist[p_actual_handle1] = db::DPoint (0.0, -5.0);
arc = &ly.cell (ly.get_pcell_variant_cell (arc->cell_index (), plist)); arc = ly.get_pcell_variant_cell (arc, plist);
// as the handle is an input parameter, only after Library::refresh the other parameters get updated // as the handle is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_start_angle].to_double (), 0.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_start_angle].to_double (), 0.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (arc->bbox ().to_string (), "(0,-9000;9000,9000)"); EXPECT_EQ (ly.cell (arc).bbox ().to_string (), "(0,-9000;9000,9000)");
EXPECT_EQ (arc->get_display_name (), "Basic.ARC(l=1/0,r=5..9,a=-90..90,n=64)"); EXPECT_EQ (ly.cell (arc).get_display_name (), "Basic.ARC(l=1/0,r=5..9,a=-90..90,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle2].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_start_angle].to_double (), -90.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_start_angle].to_double (), -90.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_start_angle].to_double (), -90.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_start_angle].to_double (), -90.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_end_angle].to_double (), 90); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius1].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius1].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius2].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius2].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius2].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius2].to_double (), 9);
// change handle2 explicitly // change handle2 explicitly
plist = ly.get_pcell_parameters (arc->cell_index ()); plist = ly.get_pcell_parameters (arc);
plist[p_actual_handle2] = db::DPoint (9.0, 0.0); plist[p_actual_handle2] = db::DPoint (9.0, 0.0);
arc = &ly.cell (ly.get_pcell_variant_cell (arc->cell_index (), plist)); arc = ly.get_pcell_variant_cell (arc, plist);
// as the handle is an input parameter, only after Library::refresh the other parameters get updated // as the handle is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_end_angle].to_double (), 90.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_end_angle].to_double (), 90.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (arc->bbox ().to_string (), "(0,-9000;9000,0)"); EXPECT_EQ (ly.cell (arc).bbox ().to_string (), "(0,-9000;9000,0)");
EXPECT_EQ (arc->get_display_name (), "Basic.ARC(l=1/0,r=5..9,a=-90..0,n=64)"); EXPECT_EQ (ly.cell (arc).get_display_name (), "Basic.ARC(l=1/0,r=5..9,a=-90..0,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_handle2].to_string (), "9,0"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_handle2].to_string (), "9,0"); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_start_angle].to_double (), -90.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_start_angle].to_double (), -90.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_start_angle].to_double (), -90.0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_start_angle].to_double (), -90.0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_end_angle].to_double (), 0); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius1].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius1].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_radius2].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius2].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius2].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius2].to_double (), 9);
} }
TEST(4_Donut) TEST(4_Donut)
@ -526,98 +526,98 @@ TEST(4_Donut)
params["actual_end_angle"] = 0.0; params["actual_end_angle"] = 0.0;
db::cell_index_type lib_cell; db::cell_index_type lib_cell;
db::Cell *donut; db::cell_index_type donut;
std::vector<tl::Variant> plist; std::vector<tl::Variant> plist;
lib_cell = lib_basic->layout ().get_pcell_variant_dict (pc.second, params); lib_cell = lib_basic->layout ().get_pcell_variant_dict (pc.second, params);
donut = &ly.cell (ly.get_lib_proxy (lib_basic, lib_cell)); donut = ly.get_lib_proxy (lib_basic, lib_cell);
// change radius explicitly // change radius explicitly
// has radius 10um, but bbox isn't correct for now (because handle was not updated) // has radius 10um, but bbox isn't correct for now (because handle was not updated)
EXPECT_EQ (donut->bbox ().to_string (), "(-10000,-10000;10000,10000)"); EXPECT_EQ (ly.cell (donut).bbox ().to_string (), "(-10000,-10000;10000,10000)");
EXPECT_EQ (donut->get_display_name (), "Basic.DONUT(l=1/0,r=4..10,n=64)"); EXPECT_EQ (ly.cell (donut).get_display_name (), "Basic.DONUT(l=1/0,r=4..10,n=64)");
// only after Library::refresh the parameters get updated and bbox is correct // only after Library::refresh the parameters get updated and bbox is correct
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius1].to_double (), 0.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius1].to_double (), 0.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (donut->bbox ().to_string (), "(-10000,-10000;10000,10000)"); EXPECT_EQ (ly.cell (donut).bbox ().to_string (), "(-10000,-10000;10000,10000)");
EXPECT_EQ (donut->get_display_name (), "Basic.DONUT(l=1/0,r=4..10,n=64)"); EXPECT_EQ (ly.cell (donut).get_display_name (), "Basic.DONUT(l=1/0,r=4..10,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius1].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius1].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius1].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius1].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius2].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius2].to_double (), 10.0);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius2].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius2].to_double (), 10.0);
// change radius2 explicitly // change radius2 explicitly
plist = ly.get_pcell_parameters (donut->cell_index ()); plist = ly.get_pcell_parameters (donut);
plist[p_actual_radius2] = 9.0; plist[p_actual_radius2] = 9.0;
donut = &ly.cell (ly.get_pcell_variant_cell (donut->cell_index (), plist)); donut = ly.get_pcell_variant_cell (donut, plist);
// as the radius is an input parameter, only after Library::refresh the other parameters get updated // as the radius is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius2].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius2].to_double (), 10.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (donut->bbox ().to_string (), "(-9000,-9000;9000,9000)"); EXPECT_EQ (ly.cell (donut).bbox ().to_string (), "(-9000,-9000;9000,9000)");
EXPECT_EQ (donut->get_display_name (), "Basic.DONUT(l=1/0,r=4..9,n=64)"); EXPECT_EQ (ly.cell (donut).get_display_name (), "Basic.DONUT(l=1/0,r=4..9,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_handle1].to_string (), "-4,0"); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle1].to_string (), "-4,0");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_handle2].to_string (), "-9,0"); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle2].to_string (), "-9,0");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius1].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius1].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius1].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius1].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius2].to_double (), 9.0);
// change radius1 explicitly // change radius1 explicitly
plist = ly.get_pcell_parameters (donut->cell_index ()); plist = ly.get_pcell_parameters (donut);
plist[p_actual_radius1] = 5.0; plist[p_actual_radius1] = 5.0;
donut = &ly.cell (ly.get_pcell_variant_cell (donut->cell_index (), plist)); donut = ly.get_pcell_variant_cell (donut, plist);
// as the radius is an input parameter, only after Library::refresh the other parameters get updated // as the radius is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius1].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius1].to_double (), 4.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (donut->bbox ().to_string (), "(-9000,-9000;9000,9000)"); EXPECT_EQ (ly.cell (donut).bbox ().to_string (), "(-9000,-9000;9000,9000)");
EXPECT_EQ (donut->get_display_name (), "Basic.DONUT(l=1/0,r=5..9,n=64)"); EXPECT_EQ (ly.cell (donut).get_display_name (), "Basic.DONUT(l=1/0,r=5..9,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_handle1].to_string (), "-5,0"); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle1].to_string (), "-5,0");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_handle2].to_string (), "-9,0"); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle2].to_string (), "-9,0");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius1].to_double (), 5.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius1].to_double (), 5.0);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius1].to_double (), 5.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius1].to_double (), 5.0);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius2].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius2].to_double (), 9.0);
// change handle1 explicitly // change handle1 explicitly
plist = ly.get_pcell_parameters (donut->cell_index ()); plist = ly.get_pcell_parameters (donut);
plist[p_handle1] = db::DPoint (0.0, -5.0); plist[p_handle1] = db::DPoint (0.0, -5.0);
donut = &ly.cell (ly.get_pcell_variant_cell (donut->cell_index (), plist)); donut = ly.get_pcell_variant_cell (donut, plist);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (donut->bbox ().to_string (), "(-9000,-9000;9000,9000)"); EXPECT_EQ (ly.cell (donut).bbox ().to_string (), "(-9000,-9000;9000,9000)");
EXPECT_EQ (donut->get_display_name (), "Basic.DONUT(l=1/0,r=5..9,n=64)"); EXPECT_EQ (ly.cell (donut).get_display_name (), "Basic.DONUT(l=1/0,r=5..9,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_handle2].to_string (), "-9,0"); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle2].to_string (), "-9,0");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius1].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius1].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius2].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius2].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius2].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius2].to_double (), 9);
// change handle2 explicitly // change handle2 explicitly
plist = ly.get_pcell_parameters (donut->cell_index ()); plist = ly.get_pcell_parameters (donut);
plist[p_handle2] = db::DPoint (9.0, 0.0); plist[p_handle2] = db::DPoint (9.0, 0.0);
donut = &ly.cell (ly.get_pcell_variant_cell (donut->cell_index (), plist)); donut = ly.get_pcell_variant_cell (donut, plist);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (donut->bbox ().to_string (), "(-9000,-9000;9000,9000)"); EXPECT_EQ (ly.cell (donut).bbox ().to_string (), "(-9000,-9000;9000,9000)");
EXPECT_EQ (donut->get_display_name (), "Basic.DONUT(l=1/0,r=5..9,n=64)"); EXPECT_EQ (ly.cell (donut).get_display_name (), "Basic.DONUT(l=1/0,r=5..9,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_handle1].to_string (), "0,-5"); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_handle2].to_string (), "9,0"); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius1].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius1].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_radius2].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius2].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius2].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius2].to_double (), 9);
unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0));
@ -659,98 +659,98 @@ TEST(5_Ellipse)
params["actual_end_angle"] = 0.0; params["actual_end_angle"] = 0.0;
db::cell_index_type lib_cell; db::cell_index_type lib_cell;
db::Cell *ellipse; db::cell_index_type ellipse;
std::vector<tl::Variant> plist; std::vector<tl::Variant> plist;
lib_cell = lib_basic->layout ().get_pcell_variant_dict (pc.second, params); lib_cell = lib_basic->layout ().get_pcell_variant_dict (pc.second, params);
ellipse = &ly.cell (ly.get_lib_proxy (lib_basic, lib_cell)); ellipse = ly.get_lib_proxy (lib_basic, lib_cell);
// change radius explicitly // change radius explicitly
// has radius 10um, but bbox isn't correct for now (because handle was not updated) // has radius 10um, but bbox isn't correct for now (because handle was not updated)
EXPECT_EQ (ellipse->bbox ().to_string (), "(-4000,-10000;4000,10000)"); EXPECT_EQ (ly.cell (ellipse).bbox ().to_string (), "(-4000,-10000;4000,10000)");
EXPECT_EQ (ellipse->get_display_name (), "Basic.ELLIPSE(l=1/0,rx=4,ry=10,n=64)"); EXPECT_EQ (ly.cell (ellipse).get_display_name (), "Basic.ELLIPSE(l=1/0,rx=4,ry=10,n=64)");
// only after Library::refresh the parameters get updated and bbox is correct // only after Library::refresh the parameters get updated and bbox is correct
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_x].to_double (), 0.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_x].to_double (), 0.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (ellipse->bbox ().to_string (), "(-4000,-10000;4000,10000)"); EXPECT_EQ (ly.cell (ellipse).bbox ().to_string (), "(-4000,-10000;4000,10000)");
EXPECT_EQ (ellipse->get_display_name (), "Basic.ELLIPSE(l=1/0,rx=4,ry=10,n=64)"); EXPECT_EQ (ly.cell (ellipse).get_display_name (), "Basic.ELLIPSE(l=1/0,rx=4,ry=10,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_x].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_x].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_actual_radius_x].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_x].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_y].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_y].to_double (), 10.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_actual_radius_y].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_y].to_double (), 10.0);
// change radius2 explicitly // change radius2 explicitly
plist = ly.get_pcell_parameters (ellipse->cell_index ()); plist = ly.get_pcell_parameters (ellipse);
plist[p_actual_radius_y] = 9.0; plist[p_actual_radius_y] = 9.0;
ellipse = &ly.cell (ly.get_pcell_variant_cell (ellipse->cell_index (), plist)); ellipse = ly.get_pcell_variant_cell (ellipse, plist);
// as the radius is an input parameter, only after Library::refresh the other parameters get updated // as the radius is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_y].to_double (), 10.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_y].to_double (), 10.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (ellipse->bbox ().to_string (), "(-4000,-9000;4000,9000)"); EXPECT_EQ (ly.cell (ellipse).bbox ().to_string (), "(-4000,-9000;4000,9000)");
EXPECT_EQ (ellipse->get_display_name (), "Basic.ELLIPSE(l=1/0,rx=4,ry=9,n=64)"); EXPECT_EQ (ly.cell (ellipse).get_display_name (), "Basic.ELLIPSE(l=1/0,rx=4,ry=9,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_handle_x].to_string (), "-4,0"); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_x].to_string (), "-4,0");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_handle_y].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_y].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_x].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_x].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_actual_radius_x].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_x].to_double (), 4.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_y].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_y].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_actual_radius_y].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_y].to_double (), 9.0);
// change radius1 explicitly // change radius1 explicitly
plist = ly.get_pcell_parameters (ellipse->cell_index ()); plist = ly.get_pcell_parameters (ellipse);
plist[p_actual_radius_x] = 5.0; plist[p_actual_radius_x] = 5.0;
ellipse = &ly.cell (ly.get_pcell_variant_cell (ellipse->cell_index (), plist)); ellipse = ly.get_pcell_variant_cell (ellipse, plist);
// as the radius is an input parameter, only after Library::refresh the other parameters get updated // as the radius is an input parameter, only after Library::refresh the other parameters get updated
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_x].to_double (), 4.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_x].to_double (), 4.0);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (ellipse->bbox ().to_string (), "(-5000,-9000;5000,9000)"); EXPECT_EQ (ly.cell (ellipse).bbox ().to_string (), "(-5000,-9000;5000,9000)");
EXPECT_EQ (ellipse->get_display_name (), "Basic.ELLIPSE(l=1/0,rx=5,ry=9,n=64)"); EXPECT_EQ (ly.cell (ellipse).get_display_name (), "Basic.ELLIPSE(l=1/0,rx=5,ry=9,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_handle_x].to_string (), "-5,0"); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_x].to_string (), "-5,0");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_handle_y].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_y].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_x].to_double (), 5.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_x].to_double (), 5.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_actual_radius_x].to_double (), 5.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_x].to_double (), 5.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_y].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_y].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_actual_radius_y].to_double (), 9.0); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_y].to_double (), 9.0);
// change handle1 explicitly // change handle1 explicitly
plist = ly.get_pcell_parameters (ellipse->cell_index ()); plist = ly.get_pcell_parameters (ellipse);
plist[p_handle_x] = db::DPoint (-5.0, 0.0); plist[p_handle_x] = db::DPoint (-5.0, 0.0);
ellipse = &ly.cell (ly.get_pcell_variant_cell (ellipse->cell_index (), plist)); ellipse = ly.get_pcell_variant_cell (ellipse, plist);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (ellipse->bbox ().to_string (), "(-5000,-9000;5000,9000)"); EXPECT_EQ (ly.cell (ellipse).bbox ().to_string (), "(-5000,-9000;5000,9000)");
EXPECT_EQ (ellipse->get_display_name (), "Basic.ELLIPSE(l=1/0,rx=5,ry=9,n=64)"); EXPECT_EQ (ly.cell (ellipse).get_display_name (), "Basic.ELLIPSE(l=1/0,rx=5,ry=9,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_handle_x].to_string (), "-5,0"); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_x].to_string (), "-5,0");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_handle_y].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_y].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_x].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_x].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_actual_radius_x].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_x].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_y].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_y].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_actual_radius_y].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_y].to_double (), 9);
// change handle2 explicitly // change handle2 explicitly
plist = ly.get_pcell_parameters (ellipse->cell_index ()); plist = ly.get_pcell_parameters (ellipse);
plist[p_handle_y] = db::DPoint (0.0, 9.0); plist[p_handle_y] = db::DPoint (0.0, 9.0);
ellipse = &ly.cell (ly.get_pcell_variant_cell (ellipse->cell_index (), plist)); ellipse = ly.get_pcell_variant_cell (ellipse, plist);
lib_basic->refresh (); lib_basic->refresh ();
EXPECT_EQ (ellipse->bbox ().to_string (), "(-5000,-9000;5000,9000)"); EXPECT_EQ (ly.cell (ellipse).bbox ().to_string (), "(-5000,-9000;5000,9000)");
EXPECT_EQ (ellipse->get_display_name (), "Basic.ELLIPSE(l=1/0,rx=5,ry=9,n=64)"); EXPECT_EQ (ly.cell (ellipse).get_display_name (), "Basic.ELLIPSE(l=1/0,rx=5,ry=9,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_handle_x].to_string (), "-5,0"); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_x].to_string (), "-5,0");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_handle_y].to_string (), "0,9"); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_y].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_x].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_x].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_actual_radius_x].to_double (), 5); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_x].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_radius_y].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_radius_y].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (ellipse->cell_index ()) [p_actual_radius_y].to_double (), 9); EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_y].to_double (), 9);
unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0));

View File

@ -173,6 +173,11 @@ public:
return true; return true;
} }
virtual bool supports_context () const
{
return false;
}
virtual tl::XMLElementBase *xml_reader_options_element () const virtual tl::XMLElementBase *xml_reader_options_element () const
{ {
return new db::ReaderOptionsXMLElement<db::CIFReaderOptions> ("cif", return new db::ReaderOptionsXMLElement<db::CIFReaderOptions> ("cif",

View File

@ -140,6 +140,11 @@ public:
return true; return true;
} }
virtual bool supports_context () const
{
return false;
}
virtual tl::XMLElementBase *xml_reader_options_element () const virtual tl::XMLElementBase *xml_reader_options_element () const
{ {
return new db::ReaderOptionsXMLElement<db::DXFReaderOptions> ("dxf", return new db::ReaderOptionsXMLElement<db::DXFReaderOptions> ("dxf",

View File

@ -89,6 +89,11 @@ class GDS2TextFormatDeclaration
{ {
return true; return true;
} }
virtual bool supports_context () const
{
return true;
}
}; };
static tl::RegisteredClass<db::StreamFormatDeclaration> format_txt_decl (new GDS2TextFormatDeclaration(), 1, "GDS2Text"); static tl::RegisteredClass<db::StreamFormatDeclaration> format_txt_decl (new GDS2TextFormatDeclaration(), 1, "GDS2Text");

View File

@ -66,6 +66,11 @@ class GDS2FormatDeclaration
return true; return true;
} }
virtual bool supports_context () const
{
return true;
}
virtual tl::XMLElementBase *xml_writer_options_element () const virtual tl::XMLElementBase *xml_writer_options_element () const
{ {
return new db::WriterOptionsXMLElement<db::GDS2WriterOptions> ("gds2", return new db::WriterOptionsXMLElement<db::GDS2WriterOptions> ("gds2",

View File

@ -338,7 +338,7 @@ GDS2WriterBase::write_shape (const db::Layout &layout, int layer, int datatype,
} }
void void
GDS2WriterBase::write_cell (db::Layout &layout, const db::Cell &cref, const std::vector <std::pair <unsigned int, db::LayerProperties> > &layers, const std::set<db::cell_index_type> &cell_set, double sf, short *time_data) GDS2WriterBase::write_cell (db::Layout &layout, const db::Cell &cref, const std::vector <std::pair <unsigned int, db::LayerProperties> > &layers, const std::set<db::cell_index_type> &cell_set, double sf, short *time_data, bool skip_body)
{ {
// cell header // cell header
@ -363,6 +363,9 @@ GDS2WriterBase::write_cell (db::Layout &layout, const db::Cell &cref, const std:
} }
} }
// skip instances and shapes for replicas
if (! skip_body) {
// instances // instances
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
@ -415,6 +418,8 @@ GDS2WriterBase::write_cell (db::Layout &layout, const db::Cell &cref, const std:
} }
}
// end of cell // end of cell
write_record_size (4); write_record_size (4);
@ -580,7 +585,8 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
if (! cref.is_real_ghost_cell () && (! cref.is_proxy () || ! cref.is_top ())) { if (! cref.is_real_ghost_cell () && (! cref.is_proxy () || ! cref.is_top ())) {
try { try {
write_cell (layout, cref, layers, cell_set, sf, time_data); bool skip_body = options.write_context_info () && cref.can_skip_replica ();
write_cell (layout, cref, layers, cell_set, sf, time_data, skip_body);
} catch (tl::Exception &ex) { } catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing cell '%s'")), layout.cell_name (*cell))); throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing cell '%s'")), layout.cell_name (*cell)));
} }

View File

@ -181,7 +181,7 @@ private:
void write_context_cell (db::Layout &layout, const short *time_data, const std::vector<cell_index_type> &cells); void write_context_cell (db::Layout &layout, const short *time_data, const std::vector<cell_index_type> &cells);
void write_context_string (size_t n, const std::string &s); void write_context_string (size_t n, const std::string &s);
void write_cell (db::Layout &layout, const db::Cell &cref, const std::vector <std::pair <unsigned int, db::LayerProperties> > &layers, void write_cell (db::Layout &layout, const db::Cell &cref, const std::vector <std::pair <unsigned int, db::LayerProperties> > &layers,
const std::set <db::cell_index_type> &cell_set, double sf, short *time_data); const std::set <db::cell_index_type> &cell_set, double sf, short *time_data, bool skip_body);
void write_shape (const db::Layout &layout, int layer, int datatype, const db::Shape &shape, double sf); void write_shape (const db::Layout &layout, int layer, int datatype, const db::Shape &shape, double sf);
}; };

View File

@ -298,6 +298,11 @@ class LEFDEFFormatDeclaration
return false; return false;
} }
virtual bool supports_context () const
{
return false;
}
virtual tl::XMLElementBase *xml_reader_options_element () const virtual tl::XMLElementBase *xml_reader_options_element () const
{ {
return new db::ReaderOptionsXMLElement<LEFDEFReaderOptions> ("lefdef", return new db::ReaderOptionsXMLElement<LEFDEFReaderOptions> ("lefdef",

View File

@ -105,6 +105,14 @@ class LStreamFormatDeclaration
return true; return true;
} }
/**
* @brief Returns a value indicating whether context information is supported
*/
virtual bool supports_context () const
{
return true;
}
virtual tl::XMLElementBase *xml_writer_options_element () const virtual tl::XMLElementBase *xml_writer_options_element () const
{ {
return new db::WriterOptionsXMLElement<lstr::WriterOptions> ("lstream", return new db::WriterOptionsXMLElement<lstr::WriterOptions> ("lstream",

View File

@ -150,7 +150,8 @@ Writer::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayou
for (auto c = mp_layout->begin_top_down (); c != mp_layout->end_top_down (); ++c) { for (auto c = mp_layout->begin_top_down (); c != mp_layout->end_top_down (); ++c) {
if (m_cells_to_write.find (*c) != m_cells_to_write.end ()) { if (m_cells_to_write.find (*c) != m_cells_to_write.end ()) {
m_cellname = layout.cell_name (*c); m_cellname = layout.cell_name (*c);
write_cell (*c, kj_stream); bool skip_body = options.write_context_info () && mp_layout->cell (*c).can_skip_replica ();
write_cell (*c, kj_stream, skip_body);
m_cellname.clear (); m_cellname.clear ();
} }
} }
@ -911,9 +912,9 @@ Writer::make_meta_data (const db::Cell *cell, stream::metaData::MetaData::Builde
* This method generates a single-view cell message (the view is only a layout view). * This method generates a single-view cell message (the view is only a layout view).
*/ */
void void
Writer::write_cell (db::cell_index_type ci, kj::BufferedOutputStream &os) Writer::write_cell (db::cell_index_type ci, kj::BufferedOutputStream &os, bool skip_body)
{ {
bool needs_layout_view = ! mp_layout->cell (ci).is_real_ghost_cell (); bool needs_layout_view = ! mp_layout->cell (ci).is_real_ghost_cell () && ! skip_body;
bool needs_meta_data_view = mp_layout->begin_meta (ci) != mp_layout->end_meta (ci); bool needs_meta_data_view = mp_layout->begin_meta (ci) != mp_layout->end_meta (ci);
capnp::MallocMessageBuilder message; capnp::MallocMessageBuilder message;

View File

@ -114,7 +114,7 @@ private:
void make_cell_hierarchy_tree (stream::library::CellHierarchyTree::Builder cell_tree); void make_cell_hierarchy_tree (stream::library::CellHierarchyTree::Builder cell_tree);
void make_meta_data (const db::Cell *cell, stream::metaData::MetaData::Builder meta_data); void make_meta_data (const db::Cell *cell, stream::metaData::MetaData::Builder meta_data);
void write_cell (db::cell_index_type ci, kj::BufferedOutputStream &os); void write_cell (db::cell_index_type ci, kj::BufferedOutputStream &os, bool skip_body);
void write_layout_view (db::cell_index_type ci, kj::BufferedOutputStream &os); void write_layout_view (db::cell_index_type ci, kj::BufferedOutputStream &os);
void write_meta_data_view (db::cell_index_type ci, kj::BufferedOutputStream &os); void write_meta_data_view (db::cell_index_type ci, kj::BufferedOutputStream &os);
void make_repetition (const RegularArray &array, stream::repetition::Repetition::Builder builder); void make_repetition (const RegularArray &array, stream::repetition::Repetition::Builder builder);

View File

@ -81,6 +81,11 @@ public:
return true; return true;
} }
virtual bool supports_context () const
{
return false;
}
virtual tl::XMLElementBase *xml_reader_options_element () const virtual tl::XMLElementBase *xml_reader_options_element () const
{ {
return new db::ReaderOptionsXMLElement<db::MAGReaderOptions> ("mag", return new db::ReaderOptionsXMLElement<db::MAGReaderOptions> ("mag",

View File

@ -161,6 +161,11 @@ public:
return false; return false;
} }
virtual bool supports_context () const
{
return false;
}
virtual tl::XMLElementBase *xml_reader_options_element () const virtual tl::XMLElementBase *xml_reader_options_element () const
{ {
return new db::ReaderOptionsXMLElement<db::MALYReaderOptions> ("maly", return new db::ReaderOptionsXMLElement<db::MALYReaderOptions> ("maly",

View File

@ -464,6 +464,11 @@ public:
return true; return true;
} }
virtual bool supports_context () const
{
return true;
}
virtual tl::XMLElementBase *xml_writer_options_element () const virtual tl::XMLElementBase *xml_writer_options_element () const
{ {
return new db::WriterOptionsXMLElement<db::OASISWriterOptions> ("oasis", return new db::WriterOptionsXMLElement<db::OASISWriterOptions> ("oasis",

View File

@ -1727,6 +1727,9 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
write_props (cref.prop_id ()); write_props (cref.prop_id ());
} }
bool skip_body = options.write_context_info () && cref.can_skip_replica ();
if (! skip_body) {
// instances // instances
if (cref.cell_instances () > 0) { if (cref.cell_instances () > 0) {
write_insts (cell_set); write_insts (cell_set);
@ -1741,6 +1744,8 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
} }
} }
}
// end CBLOCK if required // end CBLOCK if required
if (m_options.write_cblocks) { if (m_options.write_cblocks) {
end_cblock (); end_cblock ();

View File

@ -1206,6 +1206,11 @@ class GerberFormatDeclaration
{ {
return false; return false;
} }
virtual bool supports_context () const
{
return false;
}
}; };
static tl::RegisteredClass<db::StreamFormatDeclaration> format_decl (new GerberFormatDeclaration (), 1000, "GerberPCB"); static tl::RegisteredClass<db::StreamFormatDeclaration> format_decl (new GerberFormatDeclaration (), 1000, "GerberPCB");

View File

@ -4108,8 +4108,6 @@ Eval::parse (Expression &expr, const std::string &s, bool top)
void void
Eval::parse (Expression &expr, tl::Extractor &ex, bool top) Eval::parse (Expression &expr, tl::Extractor &ex, bool top)
{ {
ex.skip ();
expr = Expression (this, ex.get ()); expr = Expression (this, ex.get ());
tl::Extractor ex0 = ex; tl::Extractor ex0 = ex;

View File

@ -75,6 +75,17 @@ inline id_type id_of (const UniqueId *o)
return o ? o->m_id : 0; 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 } // namespace tl
#endif #endif

BIN
testdata/gds/lib_a.gds vendored Normal file

Binary file not shown.

BIN
testdata/gds/lib_b.gds vendored Normal file

Binary file not shown.

6
testdata/lay/a.libdef vendored Normal file
View File

@ -0,0 +1,6 @@
# A comment
define("noname.gds");
define("L2", file + ".zzz", technology = "", description = "Library L2");
define("L3", "subdir/l3.gds", technologies = [ "T2", "T3" ], replicate = false);

10
testdata/lay/b.libdef vendored Normal file
View File

@ -0,0 +1,10 @@
define("L0", "l0.gds", technology = "*");
# relative to this file
include("../lay/a.libdef");
var n4 = "L4";
var f4 = "l4.gds";
define(n4, f4);

BIN
testdata/libman/design.gds vendored Normal file

Binary file not shown.

BIN
testdata/libman/design_au1.gds vendored Normal file

Binary file not shown.

BIN
testdata/libman/design_au2.gds vendored Normal file

Binary file not shown.

BIN
testdata/libman/design_au3.gds vendored Normal file

Binary file not shown.

BIN
testdata/libman/design_au4.gds vendored Normal file

Binary file not shown.

BIN
testdata/libman/design_au5.gds vendored Normal file

Binary file not shown.

BIN
testdata/libman/libs/EX.gds vendored Normal file

Binary file not shown.

BIN
testdata/libman/libs/EX2.gds vendored Normal file

Binary file not shown.

BIN
testdata/libman/libs/NOEX.gds vendored Normal file

Binary file not shown.

BIN
testdata/libman/libs/NOEX2.gds vendored Normal file

Binary file not shown.

View File

@ -265,6 +265,70 @@ class DBLibrary_TestClass < TestBase
end end
def test_8_file_based_library
# clean up before
[ "RBA-unit-test" ].each do |name|
l = RBA::Library::library_by_name(name)
l && l.unregister
end
lylib = RBA::Layout::new
lylib.read(File.join($ut_testsrc, "testdata", "gds", "lib_a.gds"))
tmp = File::join($ut_testtmp, "rba_dbLibrary_8.gds")
lylib.write(tmp)
lib = RBA::Library::library_from_file(tmp, "RBA-unit-test")
ly = RBA::Layout::new
top = ly.create_cell("TOP")
a = ly.create_cell("A", "RBA-unit-test")
top.insert(RBA::CellInstArray::new(a, RBA::Trans::new))
assert_equal(top.dbbox.to_s, "(0,0;2,2)")
lylib.clear
lylib.read(File.join($ut_testsrc, "testdata", "gds", "lib_b.gds"))
lylib.write(tmp)
lib.refresh
assert_equal(top.dbbox.to_s, "(-1,-1;1,1)")
end
def test_9_file_based_library_multiple_files
# clean up before
[ "RBA-unit-test" ].each do |name|
l = RBA::Library::library_by_name(name)
l && l.unregister
end
files = [
File.join($ut_testsrc, "testdata", "gds", "lib_a.gds"),
File.join($ut_testsrc, "testdata", "gds", "lib_b.gds")
]
lib = RBA::Library::library_from_files(files, "RBA-unit-test")
ly = RBA::Layout::new
top = ly.create_cell("TOP")
a = ly.create_cell("A", "RBA-unit-test")
top.insert(RBA::CellInstArray::new(a, RBA::Trans::new))
assert_equal(top.dbbox.to_s, "(0,0;2,2)")
ly = RBA::Layout::new
top = ly.create_cell("TOP")
z = ly.create_cell("Z", "RBA-unit-test")
top.insert(RBA::CellInstArray::new(z, RBA::Trans::new))
assert_equal(top.dbbox.to_s, "(0,0;0.1,0.1)")
end
end end
load("test_epilogue.rb") load("test_epilogue.rb")

View File

@ -277,7 +277,7 @@ end
class DBPCellAPI_TestClass < TestBase class DBPCellAPI_TestClass < TestBase
def test_1 def _test_1
# PCellParameterDeclaration # PCellParameterDeclaration
@ -351,7 +351,7 @@ end
class DBPCell_TestClass < TestBase class DBPCell_TestClass < TestBase
def test_1 def _test_1
# instantiate and register the library # instantiate and register the library
tl = PCellTestLib::new tl = PCellTestLib::new
@ -593,7 +593,7 @@ class DBPCell_TestClass < TestBase
end end
def test_2 def _test_2
# instantiate and register the library # instantiate and register the library
tl = PCellTestLib::new tl = PCellTestLib::new
@ -632,7 +632,7 @@ class DBPCell_TestClass < TestBase
end end
def test_3 def _test_3
# instantiate and register the library # instantiate and register the library
tl = PCellTestLib::new tl = PCellTestLib::new
@ -657,7 +657,7 @@ class DBPCell_TestClass < TestBase
end end
def test_4 def _test_4
# instantiate and register the library # instantiate and register the library
tl = PCellTestLib::new tl = PCellTestLib::new
@ -674,7 +674,7 @@ class DBPCell_TestClass < TestBase
end end
def test_5 def _test_5
# instantiate and register the library # instantiate and register the library
tl = PCellTestLib::new tl = PCellTestLib::new
@ -691,7 +691,7 @@ class DBPCell_TestClass < TestBase
end end
def test_6 def _test_6
# instantiate and register the library # instantiate and register the library
tl = PCellTestLib::new tl = PCellTestLib::new
@ -707,7 +707,7 @@ class DBPCell_TestClass < TestBase
end end
def test_7 def _test_7
# instantiate and register the library # instantiate and register the library
tl = PCellTestLib::new tl = PCellTestLib::new
@ -725,7 +725,7 @@ class DBPCell_TestClass < TestBase
end end
def test_8 def _test_8
# instantiate and register the library # instantiate and register the library
tl = PCellTestLib::new tl = PCellTestLib::new
@ -747,7 +747,7 @@ class DBPCell_TestClass < TestBase
end end
def test_9 def _test_9
layout = RBA::Layout::new layout = RBA::Layout::new
@ -813,7 +813,7 @@ class DBPCell_TestClass < TestBase
end end
def test_10 def _test_10
lib = CircleLib1782::new("CircleLib") lib = CircleLib1782::new("CircleLib")
@ -864,7 +864,7 @@ class DBPCell_TestClass < TestBase
end end
def test_11 def _test_11
lib = CircleLib1782::new("CircleLib") lib = CircleLib1782::new("CircleLib")
@ -899,7 +899,7 @@ class DBPCell_TestClass < TestBase
end end
def test_12 def _test_12
if !RBA.constants.member?(:PCellDeclarationHelper) if !RBA.constants.member?(:PCellDeclarationHelper)
return return
@ -924,7 +924,7 @@ class DBPCell_TestClass < TestBase
end end
# convert to static cell # convert to static cell
def test_13 def _test_13
if !RBA.constants.member?(:PCellDeclarationHelper) if !RBA.constants.member?(:PCellDeclarationHelper)
return return
@ -958,7 +958,7 @@ end
class DBPCellParameterStates_TestClass < TestBase class DBPCellParameterStates_TestClass < TestBase
def test_1 def _test_1
ps = RBA::PCellParameterState::new ps = RBA::PCellParameterState::new