This commit is contained in:
Matthias Köfferlein 2026-04-11 20:46:03 +02:00 committed by GitHub
commit 7a64824361
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 \
dbEdges.cc \
dbEdgesLocalOperations.cc \
dbFileBasedLibrary.cc \
dbFillTool.cc \
dbFuzzyCellMapping.cc \
dbGenericShapeIterator.cc \
@ -275,6 +276,7 @@ HEADERS = \
dbEdges.h \
dbEdgesLocalOperations.h \
dbEdgesToContours.h \
dbFileBasedLibrary.h \
dbFillTool.h \
dbFuzzyCellMapping.h \
dbGenericShapeIterator.h \

View File

@ -857,7 +857,7 @@ public:
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.
* Such cells can be PCell variants or library references for example.
@ -867,6 +867,17 @@ public:
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
*

View File

@ -60,14 +60,36 @@ ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOr
}
i->second->push_back (this);
}
layout.register_cold_proxy (this);
}
ColdProxy::~ColdProxy ()
{
if (layout ()) {
layout ()->unregister_cold_proxy (this);
}
delete mp_context_info;
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 *
ColdProxy::clone (Layout &layout) const
{

View File

@ -81,6 +81,16 @@ public:
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
*/

View File

@ -692,6 +692,11 @@ public:
return false;
}
virtual bool supports_context () const
{
return false;
}
virtual tl::XMLElementBase *xml_reader_options_element () const
{
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
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::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_lib_proxy_map.clear ();
m_cold_proxy_map.clear ();
m_meta_info.clear ();
}
@ -568,6 +600,7 @@ Layout::operator= (const Layout &d)
}
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);
@ -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_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_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_shape_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
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 ());
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];
m_cell_names [id] = cp;
m_cell_map.insert (std::make_pair (cp, id));
// 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));
}
// to enforce a redraw and a rebuild
cell_name_changed ();
@ -1690,27 +1732,120 @@ Layout::cleanup (const std::set<db::cell_index_type> &keep)
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
// until there are no more cells to delete
while (true) {
// 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) {
if (cell (*c).is_proxy ()) {
if (cell (*c).is_proxy () && keep.find (*c) == keep.end ()) {
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 ()) {
break;
}
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
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;
@ -3004,9 +3147,7 @@ Layout::restore_proxies (ImportLayerMapping *layer_mapping)
}
}
if (needs_cleanup) {
cleanup ();
}
return needs_cleanup;
}
bool
@ -3145,13 +3286,32 @@ Layout::variant_name (cell_index_type cell_index) const
void
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
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
@ -3171,9 +3331,13 @@ Layout::get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_t
cell_index_type
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));
if (lp != m_lib_proxy_map.end ()) {
auto key = std::make_pair (lib->get_id (), cell_index);
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;
} else {
// create a new unique name
@ -3204,35 +3368,82 @@ 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
Layout::create_cold_proxy (const db::LayoutOrCellContextInfo &info)
{
// create a new unique name
std::string b;
if (! info.cell_name.empty ()) {
b = info.cell_name;
} else if (! info.pcell_name.empty ()) {
b = info.pcell_name;
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
std::string b;
if (! info.cell_name.empty ()) {
b = info.cell_name;
} else if (! info.pcell_name.empty ()) {
b = info.pcell_name;
}
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
b = uniquify_cell_name (b.c_str ());
}
// create a new cell (a LibraryProxy)
cell_index_type new_index = allocate_new_cell ();
ColdProxy *proxy = new ColdProxy (new_index, *this, info);
m_cells.push_back_ptr (proxy);
m_cell_ptrs [new_index] = proxy;
// enter its index and cell_name
register_cell_name (b.c_str (), new_index);
if (manager () && manager ()->transacting ()) {
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
}
return new_index;
}
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
b = uniquify_cell_name (b.c_str ());
}
// create a new cell (a LibraryProxy)
cell_index_type new_index = allocate_new_cell ();
ColdProxy *proxy = new ColdProxy (new_index, *this, info);
m_cells.push_back_ptr (proxy);
m_cell_ptrs [new_index] = proxy;
// enter its index and cell_name
register_cell_name (b.c_str (), new_index);
if (manager () && manager ()->transacting ()) {
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
}
return new_index;
}
void

View File

@ -62,6 +62,7 @@ class PCellDeclaration;
class PCellHeader;
class Library;
class LibraryProxy;
class ColdProxy;
class CellMapping;
class LayerMapping;
class Region;
@ -427,6 +428,9 @@ struct DB_PUBLIC LayoutOrCellContextInfo
std::map<std::string, tl::Variant> pcell_parameters;
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);
void serialize (std::vector<std::string> &strings);
@ -469,7 +473,8 @@ public:
typedef db::pcell_id_type pcell_id_type;
typedef std::map<std::string, pcell_id_type> pcell_name_map;
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 size_t meta_info_name_id_type;
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);
/**
* @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
*/
@ -1115,7 +1126,16 @@ public:
* Library updates may enabled lost connections which are help in cold proxies. This method will recover
* these connections.
*/
void restore_proxies(ImportLayerMapping *layer_mapping = 0);
void restore_proxies (ImportLayerMapping *layer_mapping = 0);
/**
* @brief Restores proxies as far as possible, no cleanup included
*
* This method is equivalent to "restore_proxies", but does not include a cleanup.
* Instead it returns a value of true, indicating that something got changed
* and a cleanup is required.
*/
bool restore_proxies_without_cleanup (ImportLayerMapping *layer_mapping = 0);
/**
* @brief Replaces the given cell index with the new cell
@ -1830,6 +1850,20 @@ public:
*/
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
*
@ -2144,6 +2178,7 @@ private:
std::vector<pcell_header_type *> m_pcells;
pcell_name_map m_pcell_ids;
lib_proxy_map m_lib_proxy_map;
cold_proxy_map m_cold_proxy_map;
bool m_do_cleanup;
bool m_editable;
std::map<std::string, meta_info_name_id_type> m_meta_info_name_map;

View File

@ -26,20 +26,24 @@
#include "dbPCellDeclaration.h"
#include "dbPCellVariant.h"
#include "dbLibraryManager.h"
#include "tlTimer.h"
#include <limits>
namespace db
{
Library::Library()
: m_id (std::numeric_limits<lib_id_type>::max ()), m_layout (true)
Library::Library ()
: m_id (std::numeric_limits<lib_id_type>::max ()), m_layout (true), m_replicate (true)
{
m_layout.set_library (this);
}
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)
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),
m_replicate (d.m_replicate)
{
m_layout.set_library (this);
}
@ -64,6 +68,12 @@ Library::for_technologies () const
return ! m_technologies.empty ();
}
void
Library::set_technologies (const std::set<std::string> &t)
{
m_technologies = t;
}
void
Library::set_technology (const std::string &t)
{
@ -108,10 +118,12 @@ Library::unregister_proxy (db::LibraryProxy *lib_proxy, db::Layout *ly)
if (! --c->second) {
db::cell_index_type ci = c->first;
m_refcount.erase (c);
// remove cells which are itself proxies and are no longer used
db::Cell *lib_cell = &layout ().cell (ci);
if (lib_cell && lib_cell->is_proxy () && lib_cell->parent_cells () == 0) {
layout ().delete_cell (ci);
if (layout ().is_valid_cell_index (ci)) {
// remove cells which are itself proxies and are no longer used
db::Cell *lib_cell = &layout ().cell (ci);
if (lib_cell && lib_cell->is_proxy () && lib_cell->parent_cells () == 0) {
layout ().delete_cell (ci);
}
}
}
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);
}
void
Library::set_replicate (bool f)
{
m_replicate = f;
}
void
Library::rename (const std::string &name)
{
@ -164,122 +182,175 @@ Library::rename (const std::string &name)
void
Library::refresh ()
{
std::string name = reload ();
rename (name);
refresh_without_restore ();
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);
}
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.
// 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) {
referrers.push_back (*r);
referrers.push_back (r->first);
}
// Sort for deterministic order of resolution
std::sort (referrers.begin (), referrers.end (), tl::sort_by_id ());
// Remember the layouts that will finally need a cleanup
std::set<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) {
std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> > pcells_to_map;
std::vector<db::LibraryProxy *> lib_cells_to_map;
any = false;
for (db::Layout::iterator c = r->first->begin (); c != r->first->end (); ++c) {
for (std::vector<db::Layout *>::const_iterator r = referrers.begin (); r != referrers.end (); ++r) {
db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (&*c);
if (lib_proxy && lib_proxy->lib_id () == get_id ()) {
std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> > pcells_to_map;
std::vector<db::LibraryProxy *> lib_cells_to_map;
for (auto c = (*r)->begin (); c != (*r)->end (); ++c) {
db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (c.operator-> ());
if (lib_proxy && lib_proxy->lib_id () == get_id ()) {
if (! original_layout->is_valid_cell_index (lib_proxy->library_cell_index ())) {
// safety feature, should not happen
continue;
}
db::Cell *lib_cell = &original_layout->cell (lib_proxy->library_cell_index ());
db::PCellVariant *lib_pcell = dynamic_cast <db::PCellVariant *> (lib_cell);
if (lib_pcell) {
pcells_to_map.push_back (std::make_pair (lib_proxy, lib_pcell));
} else {
lib_cells_to_map.push_back (lib_proxy);
}
needs_cleanup.insert (*r);
db::Cell *lib_cell = &layout ().cell (lib_proxy->library_cell_index ());
db::PCellVariant *lib_pcell = dynamic_cast <db::PCellVariant *> (lib_cell);
if (lib_pcell) {
pcells_to_map.push_back (std::make_pair (lib_proxy, lib_pcell));
} else {
lib_cells_to_map.push_back (lib_proxy);
}
needs_cleanup.insert (r->first);
}
}
// We do PCell resolution before the library proxy resolution. The reason is that
// PCells may generate library proxies in their instantiation. Hence we must instantiate
// the PCells before we can resolve them.
for (std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> >::const_iterator lp = pcells_to_map.begin (); lp != pcells_to_map.end (); ++lp) {
// We do PCell resolution before the library proxy resolution. The reason is that
// PCells may generate library proxies in their instantiation. Hence we must instantiate
// the PCells before we can resolve them.
for (std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> >::const_iterator lp = pcells_to_map.begin (); lp != pcells_to_map.end (); ++lp) {
db::cell_index_type ci = lp->first->Cell::cell_index ();
db::PCellVariant *lib_pcell = lp->second;
db::cell_index_type ci = lp->first->Cell::cell_index ();
db::PCellVariant *lib_pcell = lp->second;
std::pair<bool, pcell_id_type> pn (false, 0);
if (other) {
pn = other->layout ().pcell_by_name (original_layout->cell (lp->first->library_cell_index ()).get_basic_name ().c_str ());
}
std::pair<bool, pcell_id_type> pn (false, 0);
if (other) {
pn = other->layout ().pcell_by_name (lp->first->get_basic_name ().c_str ());
}
if (! pn.first) {
// substitute by a cold proxy
db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);
} else {
const db::PCellDeclaration *old_pcell_decl = layout ().pcell_declaration (lib_pcell->pcell_id ());
const db::PCellDeclaration *new_pcell_decl = other->layout ().pcell_declaration (pn.second);
if (! old_pcell_decl || ! new_pcell_decl) {
if (! pn.first) {
// substitute by a cold proxy
db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);
(*r)->get_context_info (ci, info);
(*r)->create_cold_proxy_as (info, ci);
} else {
db::pcell_parameters_type new_parameters = new_pcell_decl->map_parameters (lib_pcell->parameters_by_name ());
const db::PCellDeclaration *old_pcell_decl = original_layout->pcell_declaration (lib_pcell->pcell_id ());
const db::PCellDeclaration *new_pcell_decl = other->layout ().pcell_declaration (pn.second);
if (! old_pcell_decl || ! new_pcell_decl) {
// substitute by a cold proxy
db::LayoutOrCellContextInfo info;
(*r)->get_context_info (ci, info);
(*r)->create_cold_proxy_as (info, ci);
} else {
db::pcell_parameters_type new_parameters = new_pcell_decl->map_parameters (lib_pcell->parameters_by_name ());
// coerce the new parameters if requested
try {
db::pcell_parameters_type plist = new_parameters;
new_pcell_decl->coerce_parameters (other->layout (), plist);
plist.swap (new_parameters);
} catch (tl::Exception &ex) {
// ignore exception - we will do that again on update() to establish an error message
tl::error << ex.msg ();
}
lp->first->remap (other->get_id (), other->layout ().get_pcell_variant (pn.second, new_parameters));
// coerce the new parameters if requested
try {
db::pcell_parameters_type plist = new_parameters;
new_pcell_decl->coerce_parameters (other->layout (), plist);
plist.swap (new_parameters);
} catch (tl::Exception &ex) {
// ignore exception - we will do that again on update() to establish an error message
tl::error << ex.msg ();
}
lp->first->remap (other->get_id (), other->layout ().get_pcell_variant (pn.second, new_parameters));
}
}
}
for (std::vector<db::LibraryProxy *>::const_iterator lp = lib_cells_to_map.begin (); lp != lib_cells_to_map.end (); ++lp) {
for (std::vector<db::LibraryProxy *>::const_iterator lp = lib_cells_to_map.begin (); lp != lib_cells_to_map.end (); ++lp) {
db::cell_index_type ci = (*lp)->Cell::cell_index ();
db::cell_index_type ci = (*lp)->Cell::cell_index ();
std::pair<bool, cell_index_type> cn (false, 0);
if (other) {
cn = other->layout ().cell_by_name (original_layout->cell_name ((*lp)->library_cell_index ()));
}
std::pair<bool, cell_index_type> cn (false, 0);
if (other) {
cn = other->layout ().cell_by_name ((*lp)->get_basic_name ().c_str ());
}
if (! cn.first) {
if (! cn.first) {
// substitute by a cold proxy
db::LayoutOrCellContextInfo info;
(*r)->get_context_info (ci, info);
(*r)->create_cold_proxy_as (info, ci);
// substitute by a cold proxy
db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);
} else {
} else {
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);
}
}

View File

@ -53,12 +53,12 @@ public:
/**
* @brief The constructor
*/
Library();
Library ();
/**
* @brief Copy constructor
*/
Library(const Library &);
Library (const Library &);
/**
* @brief The destructor
@ -138,6 +138,13 @@ public:
*/
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
*
@ -172,6 +179,27 @@ public:
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
*/
@ -225,6 +253,13 @@ public:
*/
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
*
@ -237,8 +272,11 @@ public:
* @brief Remap the library proxies to a different library
*
* 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
@ -253,6 +291,7 @@ private:
db::Layout m_layout;
std::map<db::Layout *, int> m_referrers;
std::map<db::cell_index_type, int> m_refcount, m_retired_count;
bool m_replicate;
// no copying.
Library &operator=(const Library &);

View File

@ -38,7 +38,7 @@ namespace db
static LibraryManager *sp_instance (0);
LibraryManager &
DB_PUBLIC LibraryManager &
LibraryManager::instance ()
{
if (sp_instance == 0) {
@ -249,13 +249,38 @@ LibraryManager::register_lib (Library *library)
// "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 ());
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) {
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) {
(*l)->restore_proxies (0);
// Sort for deterministic order of resolution
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
@ -284,9 +309,27 @@ LibraryManager::lib_internal (lib_id_type id) const
void
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) {
if (*l) {
(*l)->refresh ();
(*l)->layout ().restore_proxies ();
}
}
}

View File

@ -22,6 +22,7 @@
#include "dbLibraryProxy.h"
#include "dbColdProxy.h"
#include "dbLibraryManager.h"
#include "dbLibrary.h"
#include "dbLayout.h"
@ -187,25 +188,6 @@ LibraryProxy::get_layer_indices (db::Layout &layout, db::ImportLayerMapping *lay
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
LibraryProxy::update (db::ImportLayerMapping *layer_mapping)
{
@ -215,32 +197,65 @@ LibraryProxy::update (db::ImportLayerMapping *layer_mapping)
Library *lib = LibraryManager::instance ().lib (lib_id ());
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_insts ();
for (unsigned int l = 0; l < lib->layout ().layers (); ++l) {
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::Instance new_inst = insert (*inst, cell_index_mapper);
if (need_transform) {
replace (new_inst, new_inst.cell_inst ().transformed_into (tr));
db::CellInstArray inst = i->cell_inst ();
Library *real_lib = lib;
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
LibraryProxy::get_basic_name () const
{

View File

@ -93,6 +93,14 @@ public:
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
*
@ -129,12 +137,12 @@ public:
/**
* @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
*/
void reregister ();
virtual void reregister ();
private:
lib_id_type m_lib_id;

View File

@ -57,7 +57,7 @@ SaveLayoutOptions::operator= (const SaveLayoutOptions &d)
m_format = d.m_format;
m_layers = d.m_layers;
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_cells = d.m_all_cells;
m_dbu = d.m_dbu;
@ -190,7 +190,7 @@ SaveLayoutOptions::add_cell (db::cell_index_type cell_index)
{
m_all_cells = false;
m_cells.insert (cell_index);
m_implied_childred.insert (cell_index);
m_implied_children.insert (cell_index);
}
void
@ -205,7 +205,7 @@ SaveLayoutOptions::clear_cells ()
{
m_all_cells = false;
m_cells.clear ();
m_implied_childred.clear ();
m_implied_children.clear ();
}
void
@ -213,7 +213,7 @@ SaveLayoutOptions::select_all_cells ()
{
m_all_cells = true;
m_cells.clear ();
m_implied_childred.clear ();
m_implied_children.clear ();
}
void
@ -341,21 +341,75 @@ 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
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) {
for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) {
cells.insert (cell->cell_index ());
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) {
if (cell->is_top ()) {
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 {
for (std::set <db::cell_index_type>::const_iterator c = m_cells.begin (); c != m_cells.end (); ++c) {
cells.insert (*c);
if (m_implied_childred.find (*c) != m_implied_childred.end ()) {
layout.cell (*c).collect_called_cells (cells);
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);
}
}
}

View File

@ -449,7 +449,7 @@ private:
std::string m_format;
std::map<unsigned int, db::LayerProperties> m_layers;
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_cells;
double m_dbu;

View File

@ -109,6 +109,11 @@ public:
*/
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
*

View File

@ -135,13 +135,52 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std::
db::Reader reader (stream);
reader.read (layout_au, options);
equal = db::compare_layouts (*subject, layout_au,
(n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose)
| ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0)
| ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts)
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta)
/*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/
, tolerance, 100 /*max diff lines*/);
if ((norm & WithoutCellNames) != 0) {
// in this case, the layouts need to have one top cell
db::cell_index_type top_subject = 0, top_au = 0;
size_t n_top_subject = 0, n_top_au = 0;
for (auto t = subject->begin_top_down (); t != subject->end_top_cells (); ++t) {
top_subject = *t;
++n_top_subject;
}
for (auto t = layout_au.begin_top_down (); t != layout_au.end_top_cells (); ++t) {
top_au = *t;
++n_top_au;
}
if (n_top_subject != 1) {
throw tl::Exception (tl::sprintf ("With smart cell mapping, the subject layout must have a single top cell"));
}
if (n_top_au != 1) {
throw tl::Exception (tl::sprintf ("With smart cell mapping, the reference layout must have a single top cell"));
}
equal = db::compare_layouts (*subject, top_subject, layout_au, top_au,
(n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose)
| ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0)
| ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts)
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta
| db::layout_diff::f_smart_cell_mapping)
/*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/
, tolerance, 100 /*max diff lines*/);
} else {
equal = db::compare_layouts (*subject, layout_au,
(n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose)
| ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0)
| ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts)
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta)
/*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/
, tolerance, 100 /*max diff lines*/);
}
if (equal && n > 0) {
tl::info << tl::sprintf ("Found match on golden reference variant %s", fn);
}

View File

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

View File

@ -29,6 +29,7 @@
#include "dbPCellDeclaration.h"
#include "dbLibrary.h"
#include "dbLibraryManager.h"
#include "dbFileBasedLibrary.h"
#include "tlLog.h"
namespace gsi
@ -156,11 +157,91 @@ static LibraryImpl *new_lib ()
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
*/
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"),
"@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"
@ -213,6 +294,26 @@ LibraryClass<db::Library> decl_Library ("db", "LibraryBase",
"\n"
"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"),
"@brief Renames the library\n"
"\n"

View File

@ -32,6 +32,8 @@
#include "dbReader.h"
#include "dbLayoutDiff.h"
#include "dbTestSupport.h"
#include "dbFileBasedLibrary.h"
#include "dbColdProxy.h"
#include "tlStream.h"
#include "tlStaticObjects.h"
#include "tlUnitTest.h"
@ -698,3 +700,137 @@ TEST(6_issue996)
CHECKPOINT ();
db::compare_layouts (this, ly, tl::testdata () + "/gds/lib_test6b.gds", db::NormalizationMode (db::WriteGDS2 + db::NoContext));
}
static size_t num_top_cells (const db::Layout &layout)
{
size_t n = 0;
for (auto t = layout.begin_top_down (); t != layout.end_top_cells (); ++t) {
++n;
}
return n;
}
static size_t num_cells (const db::Layout &layout)
{
size_t n = 0;
for (auto t = layout.begin_top_down (); t != layout.end_top_down (); ++t) {
++n;
}
return n;
}
static size_t num_defunct (const db::Layout &layout)
{
size_t ndefunct = 0;
for (auto c = layout.begin (); c != layout.end (); ++c) {
if (dynamic_cast<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 \
dbEdgeNeighborhoodTests.cc \
dbFillToolTests.cc \
dbLibraryTests.cc \
dbLogTests.cc \
dbObjectWithPropertiesTests.cc \
dbPLCConvexDecompositionTests.cc \

View File

@ -6,6 +6,8 @@
<title>About Libraries</title>
<keyword name="Library"/>
<keyword name="Libraries"/>
<keyword name="Lib Files"/>
<keyword name="Static Libraries"/>
<p>
Starting with version 0.22, KLayout offers a library concept.
@ -50,6 +52,22 @@
name of the library file minus the extension.
</p>
</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>
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.
@ -65,5 +83,186 @@
about packages.
</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>

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>611</width>
<height>271</height>
<width>619</width>
<height>475</height>
</rect>
</property>
<property name="windowTitle">
@ -154,6 +154,32 @@
</layout>
</widget>
</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>
</widget>
<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_assistant_bookmarks ("assistant-bookmarks");
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 "laySaltController.h"
#include "layConfig.h"
#include "layMainWindow.h"
#include "layQtTools.h"
#include "dbLibraryManager.h"
#include "dbLibrary.h"
#include "dbReader.h"
#include "dbCellMapping.h"
#include "dbFileBasedLibrary.h"
#include "tlLog.h"
#include "tlStream.h"
#include "tlFileUtils.h"
#include "tlEnv.h"
#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 ()
: 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
// macros. We'll do that later again in order to pull in the libraries from the packages.
sync_files ();
sync_files (false);
}
void
@ -144,7 +69,7 @@ LibraryController::initialized (lay::Dispatcher * /*root*/)
connect (m_file_watcher, SIGNAL (fileRemoved (const QString &)), this, SLOT (file_watcher_triggered ()));
}
sync_files ();
sync_files (false);
}
void
@ -163,9 +88,9 @@ LibraryController::uninitialize (lay::Dispatcher * /*root*/)
}
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
@ -175,8 +100,21 @@ LibraryController::get_menu_entries (std::vector<lay::MenuEntry> & /*menu_entrie
}
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;
}
@ -194,8 +132,24 @@ LibraryController::can_exit (lay::Dispatcher * /*root*/) const
}
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) {
m_file_watcher->clear ();
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
for (std::vector <std::pair<std::string, std::string> >::const_iterator p = paths.begin (); p != paths.end (); ++p) {
@ -245,108 +238,20 @@ LibraryController::sync_files ()
name_filters << QString::fromUtf8 ("*");
// 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) {
std::string lib_path = tl::to_string (lp.absoluteFilePath (*im));
QFileInfo fi (tl::to_qstring (lib_path));
auto ll = m_lib_files.find (lib_path);
if (ll == m_lib_files.end ()) {
needs_load = true;
} else if (fi.lastModified () > ll->second.time) {
needs_load = true;
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);
}
}
if (! needs_load) {
// If not reloading, register the existing files as known ones - this allows detecting if
// a file got removed.
for (QStringList::const_iterator im = libs.begin (); im != libs.end () && ! needs_load; ++im) {
std::string lib_path = tl::to_string (lp.absoluteFilePath (*im));
auto ll = m_lib_files.find (lib_path);
if (ll != m_lib_files.end ()) {
new_lib_files.insert (*ll);
}
}
} else {
std::map<std::string, FileBasedLibrary *> libs_by_name_here;
// Reload all files
for (QStringList::const_iterator im = libs.begin (); im != libs.end (); ++im) {
std::string lib_path = tl::to_string (lp.absoluteFilePath (*im));
QFileInfo fi (tl::to_qstring (lib_path));
try {
std::unique_ptr<FileBasedLibrary> lib (new FileBasedLibrary (lib_path));
if (! p->second.empty ()) {
lib->set_technology (p->second);
}
tl::log << "Reading library '" << lib_path << "'";
std::string libname = lib->reload ();
// merge with existing lib if there is already one in this folder with the right name
auto il = libs_by_name_here.find (libname);
if (il != libs_by_name_here.end ()) {
tl::log << "Merging with other library file with the same name: " << libname;
il->second->merge_with_other_layout (lib_path);
// now, we can forget the new library as it is included in the first one
} else {
// otherwise register the new library
if (! p->second.empty ()) {
tl::log << "Registering as '" << libname << "' for tech '" << p->second << "'";
} else {
tl::log << "Registering as '" << libname << "'";
}
LibInfo li;
li.name = libname;
li.time = fi.lastModified ();
if (! p->second.empty ()) {
li.tech.insert (p->second);
}
new_lib_files.insert (std::make_pair (lib_path, li));
lib->set_name (libname);
libs_by_name_here.insert (std::make_pair (libname, lib.release ()));
}
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
}
}
// Register the libs (NOTE: this needs to happen after the merge)
for (auto l = libs_by_name_here.begin (); l != libs_by_name_here.end (); ++l) {
try {
db::LibraryManager::instance ().register_lib (l->second);
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
} catch (...) {
}
}
}
read_libs (libs, new_lib_files, always);
}
@ -365,22 +270,30 @@ LibraryController::sync_files ()
}
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 {
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));
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
@ -388,18 +301,277 @@ LibraryController::sync_files ()
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));
auto ll = m_lib_files.find (lib_path);
if (ll == m_lib_files.end ()) {
needs_load = true;
} else if (fi.lastModified () > ll->second.time || im->tech != ll->second.tech || im->replicate != ll->second.replicate) {
needs_load = true;
}
}
if (! needs_load) {
// If not reloading, register the existing files as known ones - this allows detecting if
// a file got removed.
for (auto im = libs.begin (); im != libs.end () && ! needs_load; ++im) {
const std::string &lib_path = im->path;
auto ll = m_lib_files.find (lib_path);
if (ll != m_lib_files.end ()) {
new_lib_files.insert (*ll);
}
}
} else {
std::map<std::string, db::FileBasedLibrary *> libs_by_name_here;
// Reload all files
for (auto im = libs.begin (); im != libs.end (); ++im) {
const std::string &lib_path = im->path;
QFileInfo fi (tl::to_qstring (lib_path));
try {
std::unique_ptr<db::FileBasedLibrary> lib (new db::FileBasedLibrary (lib_path, im->name));
lib->set_technologies (im->tech);
lib->set_description (im->description);
lib->set_replicate (im->replicate);
tl::log << "Reading library '" << lib_path << "'";
std::string libname = lib->load ();
// merge with existing lib if there is already one in this folder with the right name
auto il = libs_by_name_here.find (libname);
if (il != libs_by_name_here.end ()) {
tl::log << "Merging with other library file with the same name: " << libname;
il->second->merge_with_other_layout (lib_path);
// now, we can forget the new library as it is included in the first one
} else {
// otherwise register the new library
if (! im->tech.empty ()) {
tl::log << "Registering as '" << libname << "' for tech '" << tl::join (im->tech.begin (), im->tech.end (), ",") << "'";
} else {
tl::log << "Registering as '" << libname << "'";
}
LibInfo li;
li.name = libname;
li.time = fi.lastModified ();
li.description = im->description;
li.tech = im->tech;
li.replicate = im->replicate;
new_lib_files.insert (std::make_pair (lib_path, li));
lib->set_name (libname);
libs_by_name_here.insert (std::make_pair (libname, lib.release ()));
}
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
}
}
// Register the libs (NOTE: this needs to happen after the merge)
for (auto l = libs_by_name_here.begin (); l != libs_by_name_here.end (); ++l) {
try {
db::LibraryManager::instance ().register_lib (l->second);
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
} catch (...) {
}
}
}
}
void
LibraryController::sync_with_external_sources ()
{
tl::log << tl::to_string (tr ("Package updates - updating libraries"));
m_sync_once = true;
dm_sync_files ();
}
void
LibraryController::file_watcher_triggered ()
{
tl::log << tl::to_string (tr ("Detected file system change in libraries - updating"));
dm_sync_files ();
if (m_sync) {
tl::log << tl::to_string (tr ("Detected file system change in libraries - updating"));
dm_sync_files ();
}
}
LibraryController *

View File

@ -51,12 +51,22 @@ class MainWindow;
* By making the controller a PluginDeclaration it will receive
* initialization and configuration calls.
*/
class LibraryController
class LAY_PUBLIC LibraryController
: public lay::PluginDeclaration, public tl::Object
{
Q_OBJECT
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
*/
@ -102,11 +112,23 @@ public:
*/
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
*/
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:
/**
* @brief Called when the file watcher detects a change in the file system
@ -121,17 +143,22 @@ private slots:
private:
struct LibInfo
{
LibInfo () : name (), time (), tech () { }
LibInfo () : name (), description (), time (), tech (), replicate (true) { }
std::string name;
std::string description;
QDateTime time;
std::set<std::string> tech;
bool replicate;
};
tl::FileSystemWatcher *m_file_watcher;
tl::DeferredMethod<LibraryController> dm_sync_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);
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;
root->config_get (cfg_keep_backups, kb);
mp_ui->keep_backups->setValue (kb);
@ -211,6 +215,7 @@ MainConfigPage7::commit (lay::Dispatcher *root)
{
try {
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_always_exit_without_saving, mp_ui->always_exit_without_saving->isChecked ());
} catch (...) { }

View File

@ -94,6 +94,7 @@
#include "laySettingsForm.h"
#include "laySelectCellViewForm.h"
#include "layTechnologyController.h"
#include "layLibraryController.h"
#include "laySaltController.h"
#include "layTipDialog.h"
#include "layMacroController.h"
@ -2612,6 +2613,10 @@ MainWindow::cm_writer_options ()
void
MainWindow::cm_refresh ()
{
if (lay::LibraryController::instance ()) {
lay::LibraryController::instance ()->sync_files (false);
}
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)
SOURCES = \
layLibraryControllerTests.cc \
laySalt.cc \
layHelpIndexTest.cc \
laySaltParsedURLTests.cc \

View File

@ -601,6 +601,27 @@ LayoutViewFunctions::cm_cell_convert_to_static ()
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
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;
// perform the conversion
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 ())) {
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;
db::cell_index_type lib_cell;
db::Cell *circle;
db::cell_index_type circle;
std::vector<tl::Variant> plist;
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
// has radius 10um
EXPECT_EQ (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).bbox ().to_string (), "(-10000,-10000;10000,10000)");
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (circle->cell_index ()) [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.cell (circle).bbox ().to_string (), "(-10000,-10000;10000,10000)");
EXPECT_EQ (ly.cell (circle).get_display_name (), "Basic.CIRCLE(l=1/0,r=10,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (circle) [p_radius].to_double (), 10.0);
EXPECT_EQ (ly.get_pcell_parameters (circle) [p_actual_radius].to_double (), 10.0);
// 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;
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (circle->cell_index ()) [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.cell (circle).bbox ().to_string (), "(-9000,-9000;9000,9000)");
EXPECT_EQ (ly.cell (circle).get_display_name (), "Basic.CIRCLE(l=1/0,r=9,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (circle) [p_radius].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (circle) [p_actual_radius].to_double (), 9.0);
// 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);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (circle->cell_index ()) [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.cell (circle).bbox ().to_string (), "(-8000,-8000;8000,8000)");
EXPECT_EQ (ly.cell (circle).get_display_name (), "Basic.CIRCLE(l=1/0,r=8,n=64)");
EXPECT_EQ (ly.get_pcell_parameters (circle) [p_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));
@ -145,145 +145,145 @@ TEST(2_Pie)
params["actual_end_angle"] = 0.0;
db::cell_index_type lib_cell;
db::Cell *pie;
db::cell_index_type pie;
std::vector<tl::Variant> plist;
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
// 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 (pie->get_display_name (), "Basic.PIE(l=1/0,r=10,a=-90..0,n=64)");
EXPECT_EQ (ly.cell (pie).bbox ().to_string (), "(-1000,-10000;10000,1000)");
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (pie->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (pie).bbox ().to_string (), "(0,-10000;10000,0)");
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) [p_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) [p_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) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_end_angle].to_double (), 0);
// change radius explicitly
plist = ly.get_pcell_parameters (pie->cell_index ());
plist = ly.get_pcell_parameters (pie);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (pie->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (pie).bbox ().to_string (), "(0,-9000;9000,0)");
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) [p_handle1].to_string (), "0,-9");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle1].to_string (), "0,-9");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_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) [p_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) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_end_angle].to_double (), 0);
// 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;
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (pie->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (pie).bbox ().to_string (), "(0,-9000;9000,9000)");
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) [p_handle1].to_string (), "0,-9");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle2].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) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_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) [p_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) [p_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_end_angle].to_double (), 90);
// 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;
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (pie->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (pie).bbox ().to_string (), "(0,0;9000,9000)");
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) [p_handle1].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle1].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_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) [p_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) [p_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_end_angle].to_double (), 90);
// 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);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (pie->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [p_radius].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_radius].to_double (), 9);
EXPECT_EQ (ly.cell (pie).bbox ().to_string (), "(0,-9000;9000,9000)");
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) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_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) [p_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) [p_radius].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_radius].to_double (), 9);
// 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);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (pie->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [p_radius].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (pie->cell_index ()) [p_actual_radius].to_double (), 5);
EXPECT_EQ (ly.cell (pie).bbox ().to_string (), "(0,-5000;5000,0)");
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) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_handle2].to_string (), "5,0");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_handle2].to_string (), "5,0");
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_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) [p_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) [p_radius].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (pie) [p_actual_radius].to_double (), 5);
}
TEST(3_Arc)
@ -320,182 +320,182 @@ TEST(3_Arc)
params["actual_end_angle"] = 0.0;
db::cell_index_type lib_cell;
db::Cell *arc;
db::cell_index_type arc;
std::vector<tl::Variant> plist;
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
// 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 (arc->get_display_name (), "Basic.ARC(l=1/0,r=4..10,a=-90..0,n=64)");
EXPECT_EQ (ly.cell (arc).bbox ().to_string (), "(0,-10000;10000,1000)");
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (arc->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (arc).bbox ().to_string (), "(0,-10000;10000,0)");
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) [p_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) [p_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) [p_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) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 0);
// change radius2 explicitly
plist = ly.get_pcell_parameters (arc->cell_index ());
plist = ly.get_pcell_parameters (arc);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (arc->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (arc).bbox ().to_string (), "(0,-9000;9000,0)");
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) [p_handle1].to_string (), "0,-4");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "0,-4");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_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) [p_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) [p_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) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 0);
// change radius1 explicitly
plist = ly.get_pcell_parameters (arc->cell_index ());
plist = ly.get_pcell_parameters (arc);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (arc->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (arc).bbox ().to_string (), "(0,-9000;9000,0)");
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) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_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) [p_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) [p_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) [p_end_angle].to_double (), 0);
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 0);
// 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;
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (arc->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (arc).bbox ().to_string (), "(0,-9000;9000,9000)");
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) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_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) [p_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) [p_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) [p_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 90);
// 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;
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (arc->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (arc).bbox ().to_string (), "(0,0;9000,9000)");
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) [p_handle1].to_string (), "5,0");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "5,0");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_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) [p_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) [p_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) [p_end_angle].to_double (), 90);
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_end_angle].to_double (), 90);
// 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);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (arc->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [p_radius2].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius2].to_double (), 9);
EXPECT_EQ (ly.cell (arc).bbox ().to_string (), "(0,-9000;9000,9000)");
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) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_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) [p_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) [p_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius2].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius2].to_double (), 9);
// 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);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (arc->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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->cell_index ()) [p_radius2].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (arc->cell_index ()) [p_actual_radius2].to_double (), 9);
EXPECT_EQ (ly.cell (arc).bbox ().to_string (), "(0,-9000;9000,0)");
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) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_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) [p_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) [p_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_radius2].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (arc) [p_actual_radius2].to_double (), 9);
}
TEST(4_Donut)
@ -526,98 +526,98 @@ TEST(4_Donut)
params["actual_end_angle"] = 0.0;
db::cell_index_type lib_cell;
db::Cell *donut;
db::cell_index_type donut;
std::vector<tl::Variant> plist;
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
// 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 (donut->get_display_name (), "Basic.DONUT(l=1/0,r=4..10,n=64)");
EXPECT_EQ (ly.cell (donut).bbox ().to_string (), "(-10000,-10000;10000,10000)");
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (donut->cell_index ()) [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->cell_index ()) [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.cell (donut).bbox ().to_string (), "(-10000,-10000;10000,10000)");
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) [p_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) [p_radius2].to_double (), 10.0);
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius2].to_double (), 10.0);
// change radius2 explicitly
plist = ly.get_pcell_parameters (donut->cell_index ());
plist = ly.get_pcell_parameters (donut);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (donut->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (donut).bbox ().to_string (), "(-9000,-9000;9000,9000)");
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) [p_handle1].to_string (), "-4,0");
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle2].to_string (), "-9,0");
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_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) [p_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius2].to_double (), 9.0);
// change radius1 explicitly
plist = ly.get_pcell_parameters (donut->cell_index ());
plist = ly.get_pcell_parameters (donut);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (donut->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (donut).bbox ().to_string (), "(-9000,-9000;9000,9000)");
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) [p_handle1].to_string (), "-5,0");
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle2].to_string (), "-9,0");
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_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) [p_radius2].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius2].to_double (), 9.0);
// 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);
donut = &ly.cell (ly.get_pcell_variant_cell (donut->cell_index (), plist));
donut = ly.get_pcell_variant_cell (donut, plist);
lib_basic->refresh ();
EXPECT_EQ (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.get_pcell_parameters (donut->cell_index ()) [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->cell_index ()) [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->cell_index ()) [p_radius2].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius2].to_double (), 9);
EXPECT_EQ (ly.cell (donut).bbox ().to_string (), "(-9000,-9000;9000,9000)");
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) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle2].to_string (), "-9,0");
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius2].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius2].to_double (), 9);
// 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);
donut = &ly.cell (ly.get_pcell_variant_cell (donut->cell_index (), plist));
donut = ly.get_pcell_variant_cell (donut, plist);
lib_basic->refresh ();
EXPECT_EQ (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.get_pcell_parameters (donut->cell_index ()) [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->cell_index ()) [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->cell_index ()) [p_radius2].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (donut->cell_index ()) [p_actual_radius2].to_double (), 9);
EXPECT_EQ (ly.cell (donut).bbox ().to_string (), "(-9000,-9000;9000,9000)");
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) [p_handle1].to_string (), "0,-5");
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_handle2].to_string (), "9,0");
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_actual_radius1].to_double (), 5);
EXPECT_EQ (ly.get_pcell_parameters (donut) [p_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));
@ -659,98 +659,98 @@ TEST(5_Ellipse)
params["actual_end_angle"] = 0.0;
db::cell_index_type lib_cell;
db::Cell *ellipse;
db::cell_index_type ellipse;
std::vector<tl::Variant> plist;
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
// 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 (ellipse->get_display_name (), "Basic.ELLIPSE(l=1/0,rx=4,ry=10,n=64)");
EXPECT_EQ (ly.cell (ellipse).bbox ().to_string (), "(-4000,-10000;4000,10000)");
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (ellipse->cell_index ()) [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->cell_index ()) [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.cell (ellipse).bbox ().to_string (), "(-4000,-10000;4000,10000)");
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) [p_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) [p_radius_y].to_double (), 10.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_y].to_double (), 10.0);
// change radius2 explicitly
plist = ly.get_pcell_parameters (ellipse->cell_index ());
plist = ly.get_pcell_parameters (ellipse);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (ellipse->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (ellipse).bbox ().to_string (), "(-4000,-9000;4000,9000)");
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) [p_handle_x].to_string (), "-4,0");
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_y].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_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) [p_radius_y].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_y].to_double (), 9.0);
// change radius1 explicitly
plist = ly.get_pcell_parameters (ellipse->cell_index ());
plist = ly.get_pcell_parameters (ellipse);
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
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 ();
EXPECT_EQ (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.get_pcell_parameters (ellipse->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (ellipse).bbox ().to_string (), "(-5000,-9000;5000,9000)");
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) [p_handle_x].to_string (), "-5,0");
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_y].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_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) [p_radius_y].to_double (), 9.0);
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_y].to_double (), 9.0);
// 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);
ellipse = &ly.cell (ly.get_pcell_variant_cell (ellipse->cell_index (), plist));
ellipse = ly.get_pcell_variant_cell (ellipse, plist);
lib_basic->refresh ();
EXPECT_EQ (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.get_pcell_parameters (ellipse->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (ellipse).bbox ().to_string (), "(-5000,-9000;5000,9000)");
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) [p_handle_x].to_string (), "-5,0");
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_y].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_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) [p_radius_y].to_double (), 9);
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_actual_radius_y].to_double (), 9);
// 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);
ellipse = &ly.cell (ly.get_pcell_variant_cell (ellipse->cell_index (), plist));
ellipse = ly.get_pcell_variant_cell (ellipse, plist);
lib_basic->refresh ();
EXPECT_EQ (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.get_pcell_parameters (ellipse->cell_index ()) [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->cell_index ()) [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->cell_index ()) [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.cell (ellipse).bbox ().to_string (), "(-5000,-9000;5000,9000)");
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) [p_handle_x].to_string (), "-5,0");
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_handle_y].to_string (), "0,9");
EXPECT_EQ (ly.get_pcell_parameters (ellipse) [p_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) [p_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));

View File

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

View File

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

View File

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

View File

@ -66,6 +66,11 @@ class GDS2FormatDeclaration
return true;
}
virtual bool supports_context () const
{
return true;
}
virtual tl::XMLElementBase *xml_writer_options_element () const
{
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
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
@ -363,51 +363,56 @@ GDS2WriterBase::write_cell (db::Layout &layout, const db::Cell &cref, const std:
}
}
// instances
// skip instances and shapes for replicas
if (! skip_body) {
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
// instances
// write only instances to selected cells
if (m_keep_instances || cell_set.find (inst->cell_index ()) != cell_set.end ()) {
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
// write only instances to selected cells
if (m_keep_instances || cell_set.find (inst->cell_index ()) != cell_set.end ()) {
progress_checkpoint ();
try {
write_inst (sf, *inst, true /*normalize*/, m_resolve_skew_arrays, layout, inst->prop_id ());
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing instances")));
}
progress_checkpoint ();
try {
write_inst (sf, *inst, true /*normalize*/, m_resolve_skew_arrays, layout, inst->prop_id ());
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing instances")));
}
}
}
// shapes
// shapes
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
if (layout.is_valid_layer (l->first) && l->second.layer >= 0 && l->second.datatype >= 0) {
if (layout.is_valid_layer (l->first) && l->second.layer >= 0 && l->second.datatype >= 0) {
int layer = l->second.layer;
if (layer > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write layer numbers larger than %d to GDS2 streams")), int (std::numeric_limits<uint16_t>::max ())));
}
int datatype = l->second.datatype;
if (datatype > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write datatype numbers larger than %d to GDS2 streams")), int (std::numeric_limits<uint16_t>::max ())));
}
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
progress_checkpoint ();
try {
write_shape (layout, layer, datatype, *shape, sf);
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing layer %d/%d")), layer, datatype));
int layer = l->second.layer;
if (layer > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write layer numbers larger than %d to GDS2 streams")), int (std::numeric_limits<uint16_t>::max ())));
}
int datatype = l->second.datatype;
if (datatype > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write datatype numbers larger than %d to GDS2 streams")), int (std::numeric_limits<uint16_t>::max ())));
}
++shape;
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
progress_checkpoint ();
try {
write_shape (layout, layer, datatype, *shape, sf);
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing layer %d/%d")), layer, datatype));
}
++shape;
}
}
@ -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 ())) {
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) {
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_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,
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);
};

View File

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

View File

@ -105,6 +105,14 @@ class LStreamFormatDeclaration
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
{
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) {
if (m_cells_to_write.find (*c) != m_cells_to_write.end ()) {
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 ();
}
}
@ -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).
*/
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);
capnp::MallocMessageBuilder message;

View File

@ -114,7 +114,7 @@ private:
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 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_meta_data_view (db::cell_index_type ci, kj::BufferedOutputStream &os);
void make_repetition (const RegularArray &array, stream::repetition::Repetition::Builder builder);

View File

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

View File

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

View File

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

View File

@ -1727,18 +1727,23 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
write_props (cref.prop_id ());
}
// instances
if (cref.cell_instances () > 0) {
write_insts (cell_set);
}
bool skip_body = options.write_context_info () && cref.can_skip_replica ();
if (! skip_body) {
// shapes
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
const db::Shapes &shapes = cref.shapes (l->first);
if (! shapes.empty ()) {
write_shapes (l->second, shapes);
m_progress.set (mp_stream->pos ());
// instances
if (cref.cell_instances () > 0) {
write_insts (cell_set);
}
// shapes
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
const db::Shapes &shapes = cref.shapes (l->first);
if (! shapes.empty ()) {
write_shapes (l->second, shapes);
m_progress.set (mp_stream->pos ());
}
}
}
// end CBLOCK if required

View File

@ -1206,6 +1206,11 @@ class GerberFormatDeclaration
{
return false;
}
virtual bool supports_context () const
{
return false;
}
};
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
Eval::parse (Expression &expr, tl::Extractor &ex, bool top)
{
ex.skip ();
expr = Expression (this, ex.get ());
tl::Extractor ex0 = ex;

View File

@ -75,6 +75,17 @@ inline id_type id_of (const UniqueId *o)
return o ? o->m_id : 0;
}
/**
* @brief A sorting operator of pointers by ID
*/
struct sort_by_id
{
bool operator () (const UniqueId *a, const UniqueId *b) const
{
return id_of (a) < id_of (b);
}
};
} // namespace tl
#endif

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
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
load("test_epilogue.rb")

View File

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