mirror of https://github.com/KLayout/klayout.git
Merge b866ac63d3 into 072b02c55c
This commit is contained in:
commit
7a64824361
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 &);
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ SOURCES = \
|
|||
dbCompoundOperationTests.cc \
|
||||
dbEdgeNeighborhoodTests.cc \
|
||||
dbFillToolTests.cc \
|
||||
dbLibraryTests.cc \
|
||||
dbLogTests.cc \
|
||||
dbObjectWithPropertiesTests.cc \
|
||||
dbPLCConvexDecompositionTests.cc \
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 "File/Refresh Libraries" 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"/>
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 (...) { }
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ TARGET = lay_tests
|
|||
include($$PWD/../../lib_ut.pri)
|
||||
|
||||
SOURCES = \
|
||||
layLibraryControllerTests.cc \
|
||||
laySalt.cc \
|
||||
layHelpIndexTest.cc \
|
||||
laySaltParsedURLTests.cc \
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -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);
|
||||
|
||||
|
|
@ -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);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue