Merge branch 'enhanced-meta-info' into wip2

This commit is contained in:
Matthias Koefferlein 2023-05-05 23:22:33 +02:00
commit 67276c4922
43 changed files with 2317 additions and 830 deletions

View File

@ -124,6 +124,7 @@ SOURCES = \
gsiDeclDbLibrary.cc \
gsiDeclDbManager.cc \
gsiDeclDbMatrix.cc \
gsiDeclDbMetaInfo.cc \
gsiDeclDbPath.cc \
gsiDeclDbPoint.cc \
gsiDeclDbPolygon.cc \

View File

@ -356,7 +356,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout &
&& (! exclude_cells || exclude_cells->find (*b) == exclude_cells->end ())
&& (! include_cells || include_cells->find (*b) != include_cells->end ())) {
db::cell_index_type new_cell = layout_a.add_cell (layout_b.cell_name (*b));
db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b);
new_cells.push_back (new_cell);
new_cells_b.push_back (*b);

View File

@ -275,6 +275,7 @@ VariantsCollectorBase::separate_variants (db::Layout &layout, db::Cell &top_cell
var_name += "$VAR" + tl::to_string (index);
ci_var = layout.add_cell (var_name.c_str ());
layout.add_meta_info (ci_var, layout.begin_meta (*c), layout.end_meta (*c));
copy_shapes (layout, ci_var, *c);
// a new entry for the variant

View File

@ -556,7 +556,7 @@ make_clip_variants (const db::Layout &layout,
for (std::map <std::pair <db::cell_index_type, db::Box>, db::cell_index_type>::iterator v = variants.begin (); v != variants.end (); ++v) {
if (v->first.second != layout.cell (v->first.first).bbox () || &layout != &target_layout) {
// need for a new cell
v->second = target_layout.add_cell (layout.cell_name (v->first.first));
v->second = target_layout.add_cell (layout, v->first.first);
} else {
v->second = v->first.first;
}

View File

@ -201,7 +201,8 @@ ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::C
cell_map.insert (std::make_pair (c->cell_index (), pc->cell_index ()));
} else {
// fallback: create a new cell
cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ()))));
db::cell_index_type ci = layout.add_cell (m_layout, c->cell_index ());
cell_map.insert (std::make_pair (c->cell_index (), ci));
}
} else {
@ -217,7 +218,8 @@ ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::C
cell_map.insert (std::make_pair (c->cell_index (), tc));
}
} else {
cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ()))));
db::cell_index_type ci = layout.add_cell (m_layout, c->cell_index ());
cell_map.insert (std::make_pair (c->cell_index (), ci));
}
}
@ -313,7 +315,7 @@ ClipboardData::cell_for_cell (const db::Layout &layout, db::cell_index_type cell
return cm->second;
}
db::cell_index_type target_cell_index = m_layout.add_cell (layout.cell_name (cell_index));
db::cell_index_type target_cell_index = m_layout.add_cell (layout, cell_index);
m_cell_index_map.insert (std::make_pair (cell_index, target_cell_index));
if (incomplete) {

View File

@ -49,8 +49,8 @@ ColdProxy::cold_proxies_per_lib_name (const std::string &libname)
}
}
ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info)
: Cell (ci, layout), mp_context_info (new ProxyContextInfo (info))
ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOrCellContextInfo &info)
: Cell (ci, layout), mp_context_info (new LayoutOrCellContextInfo (info))
{
if (! info.lib_name.empty ()) {
tl::MutexLocker locker (&s_map_mutex);

View File

@ -35,7 +35,7 @@
namespace db
{
struct ProxyContextInfo;
struct LayoutOrCellContextInfo;
/**
* @brief A cell specialization: a cold proxy representing a library or PCell which has gone out of scope
@ -53,7 +53,7 @@ public:
*
* Creates a cold proxy represented by the ProxyContextInfo data.
*/
ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info);
ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOrCellContextInfo &info);
/**
* @brief The destructor
@ -68,7 +68,7 @@ public:
/**
* @brief Get the library id
*/
const ProxyContextInfo &context_info () const
const LayoutOrCellContextInfo &context_info () const
{
return *mp_context_info;
}
@ -102,7 +102,7 @@ public:
virtual std::string get_qualified_name () const;
private:
ProxyContextInfo *mp_context_info;
LayoutOrCellContextInfo *mp_context_info;
ColdProxy (const ColdProxy &d);
ColdProxy &operator= (const ColdProxy &d);

View File

@ -384,6 +384,8 @@ struct DetectTagEdgeSink
DetectTagEdgeSink (int tag)
: fail_tag (tag), result (true) { }
virtual void put (const db::Edge &) { }
virtual void put (const db::Edge &, int tag)
{
if (tag == fail_tag) {

View File

@ -258,12 +258,12 @@ private:
// -----------------------------------------------------------------
// Implementation of the ProxyContextInfo class
ProxyContextInfo
ProxyContextInfo::deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to)
LayoutOrCellContextInfo
LayoutOrCellContextInfo::deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to)
{
ProxyContextInfo info;
LayoutOrCellContextInfo info;
for (std::vector<std::string>::const_iterator i = from; i != to; ++i) {
for (auto i = from; i != to; ++i) {
tl::Extractor ex (i->c_str ());
@ -290,6 +290,20 @@ ProxyContextInfo::deserialize (std::vector<std::string>::const_iterator from, st
info.cell_name = ex.skip ();
} else if (ex.test ("META(")) {
std::pair<std::string, std::pair<tl::Variant, std::string> > vv;
ex.read_word_or_quoted (vv.first);
if (ex.test (",")) {
ex.read_word_or_quoted (vv.second.second);
}
ex.test (")");
ex.test ("=");
ex.read (vv.second.first);
info.meta_info.insert(vv);
}
}
@ -298,12 +312,12 @@ ProxyContextInfo::deserialize (std::vector<std::string>::const_iterator from, st
}
void
ProxyContextInfo::serialize (std::vector<std::string> &strings)
LayoutOrCellContextInfo::serialize (std::vector<std::string> &strings)
{
if (! lib_name.empty ()) {
strings.push_back ("LIB=" + lib_name);
}
for (std::map<std::string, tl::Variant> ::const_iterator p = pcell_parameters.begin (); p != pcell_parameters.end (); ++p) {
for (auto p = pcell_parameters.begin (); p != pcell_parameters.end (); ++p) {
strings.push_back ("P(" + tl::to_word_or_quoted_string (p->first) + ")=" + p->second.to_parsable_string ());
}
if (! pcell_name.empty ()) {
@ -312,6 +326,32 @@ ProxyContextInfo::serialize (std::vector<std::string> &strings)
if (! cell_name.empty ()) {
strings.push_back ("CELL=" + cell_name);
}
std::string mv;
for (auto m = meta_info.begin (); m != meta_info.end (); ++m) {
mv.clear ();
mv += "META(";
mv += tl::to_word_or_quoted_string (m->first);
if (! m->second.second.empty ()) {
mv += ",";
mv += tl::to_word_or_quoted_string (m->second.second);
}
mv += ")=";
mv += m->second.first.to_parsable_string ();
strings.push_back (mv);
}
}
bool
LayoutOrCellContextInfo::has_proxy_info () const
{
return !pcell_name.empty () || !lib_name.empty ();
}
bool
LayoutOrCellContextInfo::has_meta_info () const
{
return !meta_info.empty ();
}
// -----------------------------------------------------------------
@ -481,7 +521,13 @@ Layout::operator= (const Layout &d)
}
m_dbu = d.m_dbu;
m_meta_info = d.m_meta_info;
m_meta_info_by_cell = d.m_meta_info_by_cell;
m_meta_info_names = d.m_meta_info_names;
m_meta_info_name_map = d.m_meta_info_name_map;
m_tech_name = d.m_tech_name;
}
return *this;
@ -593,7 +639,7 @@ Layout::set_technology_name (const std::string &tech)
if (! pn.first) {
// substitute by a cold proxy
db::ProxyContextInfo info;
db::LayoutOrCellContextInfo info;
get_context_info (ci, info);
create_cold_proxy_as (info, ci);
@ -606,7 +652,7 @@ Layout::set_technology_name (const std::string &tech)
if (! old_pcell_decl || ! new_pcell_decl) {
// substitute by a cold proxy
db::ProxyContextInfo info;
db::LayoutOrCellContextInfo info;
get_context_info (ci, info);
create_cold_proxy_as (info, ci);
@ -633,7 +679,7 @@ Layout::set_technology_name (const std::string &tech)
if (! cn.first) {
// unlink this proxy: substitute by a cold proxy
db::ProxyContextInfo info;
db::LayoutOrCellContextInfo info;
get_context_info (ci, info);
create_cold_proxy_as (info, ci);
@ -650,7 +696,7 @@ Layout::set_technology_name (const std::string &tech)
db::cell_index_type ci = (*lp)->Cell::cell_index ();
// substitute by a cold proxy
db::ProxyContextInfo info;
db::LayoutOrCellContextInfo info;
get_context_info (ci, info);
create_cold_proxy_as (info, ci);
@ -1204,6 +1250,11 @@ Layout::take_cell (cell_index_type ci)
m_cell_ptrs [ci] = 0;
auto mi = m_meta_info_by_cell.find (ci);
if (mi != m_meta_info_by_cell.end ()) {
m_meta_info_by_cell.erase (mi);
}
// Using free cell indices does have one significant drawback:
// The cellview references cannot be uniquely classified as being invalid - because the
// ID might be reused. This causes problems, when a cell is being deleted and subsequently a
@ -1252,7 +1303,24 @@ Layout::uniquify_cell_name (const char *name) const
}
}
cell_index_type
cell_index_type
Layout::add_cell (const db::Layout &other, db::cell_index_type ci)
{
cell_index_type ci_new = add_cell (other.cell_name (ci));
cell (ci_new).set_ghost_cell (other.cell (ci).is_ghost_cell ());
if (&other == this) {
add_meta_info (ci_new, other.begin_meta (ci), other.end_meta (ci));
} else {
for (auto m = other.begin_meta (ci); m != other.end_meta (ci); ++m) {
add_meta_info (ci_new, meta_info_name_id (other.meta_info_name (m->first)), m->second);
}
}
return ci_new;
}
cell_index_type
Layout::add_cell (const char *name)
{
std::string b;
@ -1748,6 +1816,58 @@ Layout::do_update ()
delete pr;
}
static Layout::meta_info_map s_empty_meta;
Layout::meta_info_iterator
Layout::begin_meta (db::cell_index_type ci) const
{
auto m = m_meta_info_by_cell.find (ci);
if (m != m_meta_info_by_cell.end ()) {
return m->second.begin ();
} else {
return s_empty_meta.begin ();
}
}
Layout::meta_info_iterator
Layout::end_meta (db::cell_index_type ci) const
{
auto m = m_meta_info_by_cell.find (ci);
if (m != m_meta_info_by_cell.end ()) {
return m->second.end ();
} else {
return s_empty_meta.end ();
}
}
const std::string &
Layout::meta_info_name (Layout::meta_info_name_id_type name_id) const
{
static std::string empty;
return name_id < m_meta_info_names.size () ? m_meta_info_names[name_id] : empty;
}
Layout::meta_info_name_id_type
Layout::meta_info_name_id (const std::string &name)
{
auto n = m_meta_info_name_map.find (name);
if (n != m_meta_info_name_map.end ()) {
return n->second;
} else {
size_t id = m_meta_info_names.size ();
m_meta_info_names.push_back (name);
m_meta_info_name_map.insert (std::make_pair (name, id));
return id;
}
}
Layout::meta_info_name_id_type
Layout::meta_info_name_id (const std::string &name) const
{
auto n = m_meta_info_name_map.find (name);
return n != m_meta_info_name_map.end () ? n->second : std::numeric_limits<meta_info_name_id_type>::max ();
}
void
Layout::clear_meta ()
{
@ -1755,42 +1875,79 @@ Layout::clear_meta ()
}
void
Layout::add_meta_info (const MetaInfo &i)
Layout::add_meta_info (meta_info_name_id_type name_id, const MetaInfo &i)
{
for (meta_info::iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) {
if (m->name == i.name) {
*m = i;
return;
}
}
m_meta_info.push_back (i);
m_meta_info[name_id] = i;
}
void
Layout::remove_meta_info (const std::string &name)
Layout::remove_meta_info (meta_info_name_id_type name_id)
{
for (meta_info::iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) {
if (m->name == name) {
m_meta_info.erase (m);
return;
}
m_meta_info.erase (name_id);
}
const MetaInfo &
Layout::meta_info (meta_info_name_id_type name_id) const
{
auto n = m_meta_info.find (name_id);
static MetaInfo null_value;
return n != m_meta_info.end () ? n->second : null_value;
}
bool
Layout::has_meta_info (meta_info_name_id_type name_id) const
{
return m_meta_info.find (name_id) != m_meta_info.end ();
}
void
Layout::clear_meta (db::cell_index_type ci)
{
m_meta_info_by_cell.erase (ci);
}
void
Layout::add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i)
{
m_meta_info_by_cell[ci][name_id] = i;
}
void
Layout::remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id)
{
auto c = m_meta_info_by_cell.find (ci);
if (c != m_meta_info_by_cell.end ()) {
c->second.erase (name_id);
}
}
const std::string &
Layout::meta_info_value (const std::string &name) const
const MetaInfo &
Layout::meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const
{
for (meta_info::const_iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) {
if (m->name == name) {
return m->value;
auto c = m_meta_info_by_cell.find (ci);
if (c != m_meta_info_by_cell.end ()) {
auto i = c->second.find (name_id);
if (i != c->second.end ()) {
return i->second;
}
}
static const std::string s_empty;
return s_empty;
static MetaInfo null_value;
return null_value;
}
void
bool
Layout::has_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const
{
auto c = m_meta_info_by_cell.find (ci);
if (c != m_meta_info_by_cell.end ()) {
return c->second.find (name_id) != c->second.end ();
} else {
return false;
}
}
void
Layout::swap_layers (unsigned int a, unsigned int b)
{
tl_assert (m_layers.layer_state (a) != LayoutLayers::Free);
@ -2369,10 +2526,85 @@ Layout::get_pcell_variant_cell (cell_index_type cell_index, const std::vector<tl
}
bool
Layout::has_context_info () const
{
for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) {
if (i->second.persisted) {
return true;
}
}
return false;
}
bool
Layout::has_context_info (cell_index_type cell_index) const
{
auto c = m_meta_info_by_cell.find (cell_index);
if (c != m_meta_info_by_cell.end ()) {
for (auto i = c->second.begin (); i != c->second.end (); ++i) {
if (i->second.persisted) {
return true;
}
}
}
const db::Cell &cref = cell (cell_index);
if (cref.is_proxy () && ! cref.is_top ()) {
return true;
} else {
return false;
}
}
bool
Layout::get_context_info (std::vector <std::string> &strings) const
{
LayoutOrCellContextInfo info;
if (! get_context_info (info)) {
return false;
} else {
info.serialize (strings);
return true;
}
}
bool
Layout::get_context_info (LayoutOrCellContextInfo &info) const
{
for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) {
if (i->second.persisted) {
std::pair<tl::Variant, std::string> &mi = info.meta_info [m_meta_info_names [i->first] ];
mi.first = i->second.value;
mi.second = i->second.description;
}
}
return true;
}
void
Layout::fill_meta_info_from_context (std::vector <std::string>::const_iterator from, std::vector <std::string>::const_iterator to)
{
fill_meta_info_from_context (LayoutOrCellContextInfo::deserialize (from, to));
}
void
Layout::fill_meta_info_from_context (const LayoutOrCellContextInfo &context_info)
{
if (! context_info.meta_info.empty ()) {
for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) {
meta_info_name_id_type name_id = meta_info_name_id (i->first);
m_meta_info [name_id] = MetaInfo (i->second.second, i->second.first, true);
}
}
}
bool
Layout::get_context_info (cell_index_type cell_index, std::vector <std::string> &strings) const
{
ProxyContextInfo info;
LayoutOrCellContextInfo info;
if (! get_context_info (cell_index, info)) {
return false;
} else {
@ -2382,8 +2614,22 @@ Layout::get_context_info (cell_index_type cell_index, std::vector <std::string>
}
bool
Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) const
Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &info) const
{
bool any_meta = false;
auto cmi = m_meta_info_by_cell.find (cell_index);
if (cmi != m_meta_info_by_cell.end ()) {
for (auto i = cmi->second.begin (); i != cmi->second.end (); ++i) {
if (i->second.persisted) {
std::pair<tl::Variant, std::string> &mi = info.meta_info [m_meta_info_names [i->first] ];
mi.first = i->second.value;
mi.second = i->second.description;
any_meta = true;
}
}
}
const db::Cell *cptr = &cell (cell_index);
const db::ColdProxy *cold_proxy = dynamic_cast <const db::ColdProxy *> (cptr);
@ -2399,7 +2645,7 @@ Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) co
const db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ());
if (! lib) {
return false; // abort
return any_meta; // abort
} else {
// one level of library indirection
@ -2432,6 +2678,27 @@ Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) co
return true;
}
void
Layout::fill_meta_info_from_context (cell_index_type cell_index, std::vector <std::string>::const_iterator from, std::vector <std::string>::const_iterator to)
{
fill_meta_info_from_context (cell_index, LayoutOrCellContextInfo::deserialize (from, to));
}
void
Layout::fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info)
{
if (! context_info.meta_info.empty ()) {
meta_info_map &mi = m_meta_info_by_cell [cell_index];
for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) {
meta_info_name_id_type name_id = meta_info_name_id (i->first);
mi [name_id] = MetaInfo (i->second.second, i->second.first, true);
}
}
}
void
Layout::restore_proxies (ImportLayerMapping *layer_mapping)
{
@ -2463,11 +2730,11 @@ Layout::recover_proxy_as (cell_index_type cell_index, std::vector <std::string>:
return false;
}
return recover_proxy_as (cell_index, ProxyContextInfo::deserialize (from, to), layer_mapping);
return recover_proxy_as (cell_index, LayoutOrCellContextInfo::deserialize (from, to), layer_mapping);
}
bool
Layout::recover_proxy_as (cell_index_type cell_index, const ProxyContextInfo &info, ImportLayerMapping *layer_mapping)
Layout::recover_proxy_as (cell_index_type cell_index, const LayoutOrCellContextInfo &info, ImportLayerMapping *layer_mapping)
{
if (! info.lib_name.empty ()) {
@ -2517,11 +2784,11 @@ Layout::recover_proxy (std::vector <std::string>::const_iterator from, std::vect
return 0;
}
return recover_proxy (ProxyContextInfo::deserialize (from, to));
return recover_proxy (LayoutOrCellContextInfo::deserialize (from, to));
}
db::Cell *
Layout::recover_proxy (const ProxyContextInfo &info)
Layout::recover_proxy (const LayoutOrCellContextInfo &info)
{
if (! info.lib_name.empty ()) {
@ -2549,7 +2816,7 @@ Layout::recover_proxy (const ProxyContextInfo &info)
}
db::Cell *
Layout::recover_proxy_no_lib (const ProxyContextInfo &info)
Layout::recover_proxy_no_lib (const LayoutOrCellContextInfo &info)
{
if (! info.pcell_name.empty ()) {
@ -2646,7 +2913,7 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index)
}
cell_index_type
Layout::create_cold_proxy (const db::ProxyContextInfo &info)
Layout::create_cold_proxy (const db::LayoutOrCellContextInfo &info)
{
// create a new unique name
std::string b;
@ -2677,7 +2944,7 @@ Layout::create_cold_proxy (const db::ProxyContextInfo &info)
}
void
Layout::create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type target_cell_index)
Layout::create_cold_proxy_as (const db::LayoutOrCellContextInfo &info, cell_index_type target_cell_index)
{
tl_assert (m_cell_ptrs [target_cell_index] != 0);

View File

@ -417,15 +417,19 @@ public:
/**
* @brief A binary object representing context information for regenerating library proxies and PCells
*/
struct DB_PUBLIC ProxyContextInfo
struct DB_PUBLIC LayoutOrCellContextInfo
{
std::string lib_name;
std::string cell_name;
std::string pcell_name;
std::map<std::string, tl::Variant> pcell_parameters;
std::map<std::string, std::pair<tl::Variant, std::string> > meta_info;
static ProxyContextInfo deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to);
static LayoutOrCellContextInfo deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to);
void serialize (std::vector<std::string> &strings);
bool has_proxy_info () const;
bool has_meta_info () const;
};
/**
@ -465,8 +469,9 @@ public:
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 LayerIterator layer_iterator;
typedef std::vector<MetaInfo> meta_info;
typedef meta_info::const_iterator meta_info_iterator;
typedef size_t meta_info_name_id_type;
typedef std::map<meta_info_name_id_type, MetaInfo> meta_info_map;
typedef meta_info_map::const_iterator meta_info_iterator;
/**
* @brief A helper functor to compare "const char *" by the content
@ -756,6 +761,14 @@ public:
*/
cell_index_type add_cell (const char *name = 0);
/**
* @brief Adds a cell using another cell as a template
*
* This method will use the name of the other cell and initialize the
* new cell with the meta info from the other cell.
*/
cell_index_type add_cell (const db::Layout &other, db::cell_index_type ci);
/**
* @brief Add a cell without a name
*
@ -1014,27 +1027,69 @@ public:
/**
* @brief Creates a cold proxy representing the given context information
*/
cell_index_type create_cold_proxy (const db::ProxyContextInfo &info);
cell_index_type create_cold_proxy (const db::LayoutOrCellContextInfo &info);
/**
* @brief Subsitutes the given cell by a cold proxy representing the given context information
*/
void create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type cell_index);
void create_cold_proxy_as (const db::LayoutOrCellContextInfo &info, cell_index_type cell_index);
/**
* @brief Gets a value indicating whether layout context info is provided / needed
*/
bool has_context_info() const;
/**
* @brief Gets a value indicating whether layout context info is provided / needed
*/
bool has_context_info(cell_index_type cell_index) const;
/**
* @brief Get the context information for the layout (for writing into a file)
*
* The context information is a sequence of strings which is pushed onto the given
* vector. It can be used to fill meta information with fill_meta_info_from_context.
*/
bool get_context_info (std::vector <std::string> &strings) const;
/**
* @brief Gets the context information as a binary object
*/
bool get_context_info (LayoutOrCellContextInfo &context_info) const;
/**
* @brief Fills the layout's meta information from the context
*/
void fill_meta_info_from_context (std::vector <std::string>::const_iterator from, std::vector <std::string>::const_iterator to);
/**
* @brief Fills the layout's meta information from the binary context
*/
void fill_meta_info_from_context (const LayoutOrCellContextInfo &context_info);
/**
* @brief Get the context information for a given cell (for writing into a file)
*
* The context information is a sequence of strings which is pushed onto the given
* vector. It can be used to recover a respective proxy cell with the recover_proxy method.
* If the given cell is not a valid proxy or library references are missing, the method
* will return false.
* vector. It can be used to recover a respective proxy cell with the recover_proxy method
* or to fill meta information using fill_meta_info_from_context.
*/
bool get_context_info (cell_index_type cell_index, std::vector <std::string> &context_info) const;
/**
* @brief Gets the context information as a binary object
*/
bool get_context_info (cell_index_type cell_index, ProxyContextInfo &context_info) const;
bool get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &context_info) const;
/**
* @brief Fills the layout's meta information from the context
*/
void fill_meta_info_from_context (cell_index_type cell_index, std::vector <std::string>::const_iterator from, std::vector <std::string>::const_iterator to);
/**
* @brief Fills the layout's meta information from the binary context
*/
void fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info);
/**
* @brief Recover a proxy cell from the given context info.
@ -1050,7 +1105,7 @@ public:
/**
* @brief Recover a proxy cell from the given binary context info object.
*/
db::Cell *recover_proxy (const ProxyContextInfo &context_info);
db::Cell *recover_proxy (const LayoutOrCellContextInfo &context_info);
/**
* @brief Recover a proxy cell from the given context info.
@ -1072,7 +1127,7 @@ public:
*
* See the string-based version of "recover_proxy_as" for details.
*/
bool recover_proxy_as (cell_index_type cell_index, const ProxyContextInfo &context_info, ImportLayerMapping *layer_mapping = 0);
bool recover_proxy_as (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info, ImportLayerMapping *layer_mapping = 0);
/**
* @brief Restores proxies as far as possible
@ -1812,6 +1867,31 @@ public:
return m_meta_info.end ();
}
/**
* @brief Delivers the meta information (begin iterator) per cell
*/
meta_info_iterator begin_meta (db::cell_index_type ci) const;
/**
* @brief Delivers the meta information (end iterator) per cell
*/
meta_info_iterator end_meta (db::cell_index_type ci) const;
/**
* @brief Gets the meta informatio name by ID
*/
const std::string &meta_info_name (meta_info_name_id_type name_id) const;
/**
* @brief Gets the meta information name ID for a specific string
*/
meta_info_name_id_type meta_info_name_id (const std::string &name) const;
/**
* @brief Gets the meta information name ID for a specific string (const version)
*/
meta_info_name_id_type meta_info_name_id (const std::string &name);
/**
* @brief Clears the meta information
*/
@ -1819,22 +1899,146 @@ public:
/**
* @brief Adds meta information
* The given meta information object is appended at the end of the meta information list.
* The given meta information object is added to the meta information list.
* If a meta info object with the same name already exists it is overwritten.
*/
void add_meta_info (const MetaInfo &i);
void add_meta_info (const std::string &name, const MetaInfo &i)
{
add_meta_info (meta_info_name_id (name), i);
}
/**
* @brief Adds meta information (variant with name ID)
*/
void add_meta_info (meta_info_name_id_type name_id, const MetaInfo &i);
/**
* @brief Adds meta information from a sequence
*/
template <class I>
void add_meta_info (const I &b, const I &e)
{
for (I i = b; i != e; ++i) {
m_meta_info.insert (b, e);
}
}
/**
* @brief Removes the meta information object with the given name
* The method will do nothing if no object with that name exists.
*/
void remove_meta_info (const std::string &name);
void remove_meta_info (const std::string &name)
{
remove_meta_info (meta_info_name_id (name));
}
/**
* @brief Removes the meta information object with the given name ID
*/
void remove_meta_info (meta_info_name_id_type name_id);
/**
* @brief Gets the meta info value for a meta info object with the given name
* If no object with that name exists, an empty string is returned
*/
const std::string &meta_info_value (const std::string &name) const;
const MetaInfo &meta_info (const std::string &name) const
{
return meta_info (meta_info_name_id (name));
}
/**
* @brief Gets the meta info value for a meta info object with the given name ID
*/
const MetaInfo &meta_info (meta_info_name_id_type name_id) const;
/**
* @brief Gets a value indicating whether a meta info with the given name is present
*/
bool has_meta_info (const std::string &name) const
{
return has_meta_info (meta_info_name_id (name));
}
/**
* @brief Gets a value indicating whether a meta info with the given name is present
*/
bool has_meta_info (meta_info_name_id_type name_id) const;
/**
* @brief Clears the meta information for a specific cell
*/
void clear_meta (db::cell_index_type ci);
/**
* @brief Adds meta information for a given cell
* The given meta information object is to the meta information list for the given cell.
* If a meta info object with the same name already exists it is overwritten.
*/
void add_meta_info (db::cell_index_type ci, const std::string &name, const MetaInfo &i)
{
add_meta_info (ci, meta_info_name_id (name), i);
}
/**
* @brief Adds meta information for a given cell (version with name ID)
* The given meta information object is appended at the end of the meta information list.
* If a meta info object with the same name already exists it is overwritten.
*/
void add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i);
/**
* @brief Adds meta information from a sequence
*/
template <class I>
void add_meta_info (db::cell_index_type ci, const I &b, const I &e)
{
for (I i = b; i != e; ++i) {
m_meta_info_by_cell [ci].insert (b, e);
}
}
/**
* @brief Gets a value indicating whether a meta info with the given name is present for the given cell
*/
bool has_meta_info (db::cell_index_type ci, const std::string &name) const
{
return has_meta_info (ci, meta_info_name_id (name));
}
/**
* @brief Gets a value indicating whether a meta info with the given name is present for the given cell
*/
bool has_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const;
/**
* @brief Removes the meta information object with the given name from the given cell
* The method will do nothing if no object with that name exists.
*/
void remove_meta_info (db::cell_index_type ci, const std::string &name)
{
remove_meta_info (ci, meta_info_name_id (name));
}
/**
* @brief Removes the meta information object with the given name ID from the given cell
* The method will do nothing if no object with that name exists.
*/
void remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id);
/**
* @brief Gets the meta info value for a meta info object with the given name for the given cell
* If no object with that name exists, an empty string is returned
*/
const MetaInfo &meta_info (db::cell_index_type ci, const std::string &name) const
{
return meta_info (ci, meta_info_name_id (name));
}
/**
* @brief Gets the meta info value for a meta info object with the given name ID for the given cell
* If no object with that name exists, an empty string is returned
*/
const MetaInfo &meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const;
/**
* @brief This event is triggered when the technology changes
@ -1876,7 +2080,11 @@ private:
lib_proxy_map m_lib_proxy_map;
bool m_do_cleanup;
bool m_editable;
meta_info m_meta_info;
std::map<std::string, meta_info_name_id_type> m_meta_info_name_map;
std::vector<std::string> m_meta_info_names;
meta_info_map m_meta_info;
std::map<db::cell_index_type, meta_info_map> m_meta_info_by_cell;
std::string m_tech_name;
tl::Mutex m_lock;
@ -1920,7 +2128,7 @@ private:
/**
* @brief Recovers a proxy without considering the library from context_info
*/
db::Cell *recover_proxy_no_lib (const ProxyContextInfo &context_info);
db::Cell *recover_proxy_no_lib (const LayoutOrCellContextInfo &context_info);
};
/**

View File

@ -218,7 +218,7 @@ merge_layouts (db::Layout &target,
std::map<db::cell_index_type, db::cell_index_type> new_cell_mapping;
for (std::set<db::cell_index_type>::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) {
if (cell_mapping.find (*c) == cell_mapping.end ()) {
new_cell_mapping.insert (std::make_pair (*c, target.add_cell (source.cell_name (*c))));
new_cell_mapping.insert (std::make_pair (*c, target.add_cell (source, *c)));
}
}

View File

@ -205,7 +205,7 @@ Library::remap_to (db::Library *other)
if (! pn.first) {
// substitute by a cold proxy
db::ProxyContextInfo info;
db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);
@ -216,7 +216,7 @@ Library::remap_to (db::Library *other)
if (! old_pcell_decl || ! new_pcell_decl) {
// substitute by a cold proxy
db::ProxyContextInfo info;
db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);
@ -254,7 +254,7 @@ Library::remap_to (db::Library *other)
if (! cn.first) {
// substitute by a cold proxy
db::ProxyContextInfo info;
db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);

View File

@ -31,31 +31,34 @@
namespace db
{
// Switch for version-agnostic code
#define KLAYOUT_META_INFO_V2
/**
* @brief A structure describing the meta information from the reader
*
* In the meta information block, the reader provides additional information
* about the file and content etc.
* "name" is a unique name that can be used to identify the information.
* "description" is a "speaking" description of the information.
* "value" is the value of the specific part of meta information.
*/
struct DB_PUBLIC MetaInfo
{
MetaInfo (const std::string &n, const std::string &d, const std::string &v)
: name (n), description (d), value (v)
MetaInfo (const std::string &d, const tl::Variant &v, bool p = false)
: description (d), value (v), persisted (p)
{
// .. nothing else ..
}
MetaInfo ()
: persisted (false)
{
// .. nothing else ..
}
std::string name;
std::string description;
std::string value;
tl::Variant value;
bool persisted;
};
/**
@ -63,7 +66,6 @@ struct DB_PUBLIC MetaInfo
*/
inline void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, const MetaInfo &v, bool no_self, void *parent)
{
db::mem_stat (stat, purpose, cat, v.name, no_self, parent);
db::mem_stat (stat, purpose, cat, v.description, no_self, parent);
db::mem_stat (stat, purpose, cat, v.value, no_self, parent);
}

View File

@ -20,9 +20,8 @@
*/
#include "gsiDecl.h"
#include "gsiDeclDbMetaInfo.h"
#include "gsiDeclDbHelpers.h"
#include "dbLayout.h"
@ -984,6 +983,58 @@ static db::Layout *layout (db::Cell *cell)
return cell->layout ();
}
static void cell_clear_meta_info (db::Cell *cell)
{
if (cell->layout ()) {
cell->layout ()->clear_meta (cell->cell_index ());
}
}
static void cell_remove_meta_info (db::Cell *cell, const std::string &name)
{
if (cell->layout ()) {
cell->layout ()->remove_meta_info (cell->cell_index (), name);
}
}
static void cell_add_meta_info (db::Cell *cell, const MetaInfo &mi)
{
if (cell->layout ()) {
cell->layout ()->add_meta_info (cell->cell_index (), mi.name, db::MetaInfo (mi.description, mi.value));
}
}
static const tl::Variant &cell_meta_info_value (db::Cell *cell, const std::string &name)
{
if (! cell->layout ()) {
static tl::Variant null_value;
return null_value;
} else {
return cell->layout ()->meta_info (cell->cell_index (), name).value;
}
}
static MetaInfo *cell_meta_info (db::Cell *cell, const std::string &name)
{
if (! cell->layout ()) {
return 0;
} else if (cell->layout ()->has_meta_info (cell->cell_index (), name)) {
const db::MetaInfo &value = cell->layout ()->meta_info (cell->cell_index (), name);
return new MetaInfo (name, value);
} else {
return 0;
}
}
static gsi::MetaInfoIterator cell_each_meta_info (const db::Cell *cell)
{
if (! cell->layout ()) {
return gsi::MetaInfoIterator ();
} else {
return gsi::MetaInfoIterator (cell->layout (), cell->layout ()->begin_meta (cell->cell_index ()), cell->layout ()->end_meta (cell->cell_index ()));
}
}
static bool cell_has_prop_id (const db::Cell *c)
{
return c->prop_id () != 0;
@ -1780,6 +1831,47 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"This method has been introduced in version 0.23."
) +
gsi::method_ext ("add_meta_info", &cell_add_meta_info, gsi::arg ("info"),
"@brief Adds meta information to the cell\n"
"See \\LayoutMetaInfo for details about cells and meta information.\n"
"\n"
"This method has been introduced in version 0.28.8."
) +
gsi::method_ext ("clear_meta_info", &cell_clear_meta_info,
"@brief Clears the meta information of the cell\n"
"See \\LayoutMetaInfo for details about cells and meta information.\n"
"\n"
"This method has been introduced in version 0.28.8."
) +
gsi::method_ext ("remove_meta_info", &cell_remove_meta_info, gsi::arg ("name"),
"@brief Removes meta information from the cell\n"
"See \\LayoutMetaInfo for details about cells and meta information.\n"
"\n"
"This method has been introduced in version 0.28.8."
) +
gsi::method_ext ("meta_info_value", &cell_meta_info_value, gsi::arg ("name"),
"@brief Gets the meta information value for a given name\n"
"See \\LayoutMetaInfo for details about cells and meta information.\n"
"\n"
"If no meta information with the given name exists, a nil value will be returned.\n"
"A more generic version that delivers all fields of the meta information is \\meta_info.\n"
"\n"
"This method has been introduced in version 0.28.8."
) +
gsi::factory_ext ("meta_info", &cell_meta_info, gsi::arg ("name"),
"@brief Gets the meta information for a given name\n"
"See \\LayoutMetaInfo for details about cells and meta information.\n"
"\n"
"If no meta information with the given name exists, a default object with empty fields will be returned.\n"
"\n"
"This method has been introduced in version 0.28.8."
) +
gsi::iterator_ext ("each_meta_info", &cell_each_meta_info,
"@brief Iterates over the meta information of the cell\n"
"See \\LayoutMetaInfo for details about cells and meta information.\n"
"\n"
"This method has been introduced in version 0.28.8."
) +
gsi::method_ext ("write", &write_simple, gsi::arg ("file_name"),
"@brief Writes the cell to a layout file\n"
"The format of the file will be determined from the file name. Only the cell and "

View File

@ -22,6 +22,7 @@
#include "gsiDecl.h"
#include "gsiDeclDbMetaInfo.h"
#include "dbLayout.h"
#include "dbClip.h"
#include "dbRecursiveShapeIterator.h"
@ -902,39 +903,30 @@ static db::Cell *create_cell4 (db::Layout *layout, const std::string &name, cons
return &layout->cell (layout->get_lib_proxy (lib, lib_cell));
}
static db::MetaInfo *layout_meta_info_ctor (const std::string &name, const std::string &value, const std::string &description)
static void layout_add_meta_info (db::Layout *layout, const MetaInfo &mi)
{
return new db::MetaInfo (name, description, value);
layout->add_meta_info (mi.name, db::MetaInfo (mi.description, mi.value));
}
static void layout_meta_set_name (db::MetaInfo *mi, const std::string &n)
static MetaInfo *layout_get_meta_info (db::Layout *layout, const std::string &name)
{
mi->name = n;
if (layout->has_meta_info (name)) {
const db::MetaInfo &value = layout->meta_info (name);
return new MetaInfo (name, value);
} else {
return 0;
}
}
static const std::string &layout_meta_get_name (const db::MetaInfo *mi)
static const tl::Variant &layout_get_meta_info_value (db::Layout *layout, const std::string &name)
{
return mi->name;
const db::MetaInfo &value = layout->meta_info (name);
return value.value;
}
static void layout_meta_set_value (db::MetaInfo *mi, const std::string &n)
static MetaInfoIterator layout_each_meta_info (const db::Layout *layout)
{
mi->value = n;
}
static const std::string &layout_meta_get_value (const db::MetaInfo *mi)
{
return mi->value;
}
static void layout_meta_set_description (db::MetaInfo *mi, const std::string &n)
{
mi->description = n;
}
static const std::string &layout_meta_get_description (const db::MetaInfo *mi)
{
return mi->description;
return MetaInfoIterator (layout, layout->begin_meta (), layout->end_meta ());
}
static void scale_and_snap1 (db::Layout *layout, db::Cell &cell, db::Coord g, db::Coord m, db::Coord d)
@ -997,45 +989,6 @@ static void move_tree_shapes3 (db::Layout *layout, db::Layout &source_layout, co
db::move_shapes (*layout, source_layout, trans, cm.source_cells (), cm.table (), lm.table ());
}
Class<db::MetaInfo> decl_LayoutMetaInfo ("db", "LayoutMetaInfo",
gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()),
"@brief Creates a layout meta info object\n"
"@param name The name\n"
"@param value The value\n"
"@param description An optional description text\n"
) +
gsi::method_ext ("name", &layout_meta_get_name,
"@brief Gets the name of the layout meta info object\n"
) +
gsi::method_ext ("name=", &layout_meta_set_name,
"@brief Sets the name of the layout meta info object\n"
) +
gsi::method_ext ("value", &layout_meta_get_value,
"@brief Gets the value of the layout meta info object\n"
) +
gsi::method_ext ("value=", &layout_meta_set_value,
"@brief Sets the value of the layout meta info object\n"
) +
gsi::method_ext ("description", &layout_meta_get_description,
"@brief Gets the description of the layout meta info object\n"
) +
gsi::method_ext ("description=", &layout_meta_set_description,
"@brief Sets the description of the layout meta info object\n"
),
"@brief A piece of layout meta information\n"
"Layout meta information is basically additional data that can be attached to a layout. "
"Layout readers may generate meta information and some writers will add layout information to "
"the layout object. Some writers will also read meta information to determine certain attributes.\n"
"\n"
"Multiple layout meta information objects can be attached to one layout using \\Layout#add_meta_info. "
"Meta information is identified by a unique name and carries a string value plus an optional description string. "
"The description string is for information only and is not evaluated by code.\n"
"\n"
"See also \\Layout#each_meta_info and \\Layout#meta_info_value and \\Layout#remove_meta_info"
"\n"
"This class has been introduced in version 0.25."
);
static void dtransform (db::Layout *layout, const db::DTrans &trans)
{
db::CplxTrans dbu_trans (layout->dbu ());
@ -1097,27 +1050,42 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"This method has been introduced in version 0.27.9."
) +
gsi::method ("add_meta_info", &db::Layout::add_meta_info, gsi::arg ("info"),
gsi::method_ext ("add_meta_info", &layout_add_meta_info, gsi::arg ("info"),
"@brief Adds meta information to the layout\n"
"See \\LayoutMetaInfo for details about layouts and meta information."
"\n"
"This method has been introduced in version 0.25."
) +
gsi::method ("remove_meta_info", &db::Layout::remove_meta_info, gsi::arg ("name"),
gsi::method ("clear_meta_info", static_cast<void (db::Layout::*) ()> (&db::Layout::clear_meta),
"@brief Clears the meta information of the layout\n"
"See \\LayoutMetaInfo for details about layouts and meta information."
"\n"
"This method has been introduced in version 0.28.8."
) +
gsi::method ("remove_meta_info", static_cast<void (db::Layout::*) (const std::string &name)> (&db::Layout::remove_meta_info), gsi::arg ("name"),
"@brief Removes meta information from the layout\n"
"See \\LayoutMetaInfo for details about layouts and meta information."
"\n"
"This method has been introduced in version 0.25."
) +
gsi::method ("meta_info_value", &db::Layout::meta_info_value, gsi::arg ("name"),
gsi::method_ext ("meta_info_value", &layout_get_meta_info_value, gsi::arg ("name"),
"@brief Gets the meta information value for a given name\n"
"See \\LayoutMetaInfo for details about layouts and meta information.\n"
"\n"
"If no meta information with the given name exists, an empty string will be returned.\n"
"If no meta information with the given name exists, a nil value will be returned.\n"
"A more generic version that delivers all fields of the meta information is \\meta_info.\n"
"\n"
"This method has been introduced in version 0.25."
"This method has been introduced in version 0.25. Starting with version 0.28.8, the value is of variant type instead of string only.\n"
) +
gsi::iterator ("each_meta_info", &db::Layout::begin_meta, &db::Layout::end_meta,
gsi::factory_ext ("meta_info", &layout_get_meta_info, gsi::arg ("name"),
"@brief Gets the meta information for a given name\n"
"See \\LayoutMetaInfo for details about layouts and meta information.\n"
"\n"
"If no meta information with the given name exists, nil is returned.\n"
"\n"
"This method has been introduced in version 0.28.8.\n"
) +
gsi::iterator_ext ("each_meta_info", &layout_each_meta_info,
"@brief Iterates over the meta information of the layout\n"
"See \\LayoutMetaInfo for details about layouts and meta information.\n"
"\n"

View File

@ -0,0 +1,168 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2023 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 "gsiDecl.h"
#include "gsiDeclDbMetaInfo.h"
namespace gsi
{
static MetaInfo *layout_meta_info_ctor (const std::string &name, const tl::Variant &value, const std::string &description, bool persisted)
{
return new MetaInfo (name, description, value, persisted);
}
static void layout_meta_set_name (MetaInfo *mi, const std::string &n)
{
mi->name = n;
}
static const std::string &layout_meta_get_name (const MetaInfo *mi)
{
return mi->name;
}
static void layout_meta_set_value (MetaInfo *mi, const tl::Variant &n)
{
mi->value = n;
}
static const tl::Variant &layout_meta_get_value (const MetaInfo *mi)
{
return mi->value;
}
static void layout_meta_set_description (MetaInfo *mi, const std::string &n)
{
mi->description = n;
}
static const std::string &layout_meta_get_description (const MetaInfo *mi)
{
return mi->description;
}
static void layout_meta_set_persisted (MetaInfo *mi, bool f)
{
mi->persisted = f;
}
static bool layout_meta_get_persisted (const MetaInfo *mi)
{
return mi->persisted;
}
Class<MetaInfo> decl_LayoutMetaInfo ("db", "LayoutMetaInfo",
gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()), gsi::arg ("persisted", false),
"@brief Creates a layout meta info object\n"
"@param name The name\n"
"@param value The value\n"
"@param description An optional description text\n"
"@param persisted If true, the meta information will be persisted in some file formats, like GDS2\n"
"\n"
"The 'persisted' attribute has been introduced in version 0.28.8.\n"
) +
gsi::method_ext ("name", &layout_meta_get_name,
"@brief Gets the name of the layout meta info object\n"
) +
gsi::method_ext ("name=", &layout_meta_set_name, gsi::arg ("name"),
"@brief Sets the name of the layout meta info object\n"
) +
gsi::method_ext ("value", &layout_meta_get_value,
"@brief Gets the value of the layout meta info object\n"
) +
gsi::method_ext ("value=", &layout_meta_set_value, gsi::arg ("value"),
"@brief Sets the value of the layout meta info object\n"
) +
gsi::method_ext ("description", &layout_meta_get_description,
"@brief Gets the description of the layout meta info object\n"
) +
gsi::method_ext ("description=", &layout_meta_set_description, gsi::arg ("description"),
"@brief Sets the description of the layout meta info object\n"
) +
gsi::method_ext ("is_persisted?", &layout_meta_get_persisted,
"@brief Gets a value indicating whether the meta information will be persisted\n"
"This predicate was introduced in version 0.28.8.\n"
) +
gsi::method_ext ("persisted=", &layout_meta_set_persisted, gsi::arg ("flag"),
"@brief Sets a value indicating whether the meta information will be persisted\n"
"This predicate was introduced in version 0.28.8.\n"
),
"@brief A piece of layout meta information\n"
"Layout meta information is basically additional data that can be attached to a layout. "
"Layout readers may generate meta information and some writers will add layout information to "
"the layout object. Some writers will also read meta information to determine certain attributes.\n"
"\n"
"Multiple layout meta information objects can be attached to one layout using \\Layout#add_meta_info. "
"Meta information is identified by a unique name and carries a string value plus an optional description string. "
"The description string is for information only and is not evaluated by code.\n"
"\n"
"Meta information can be attached to the layout object and to cells. It is similar to "
"user properties. The differences are:\n"
"\n"
"@ul\n"
"@li Meta information is stored differently in GDS and OASIS files using the context information added "
" by KLayout to annotated PCell or library cells too. Hence meta information does not pollute "
" the standard user properties space. @/li\n"
"@li The value of meta information can be complex serializable types such as lists, hashes and elementary "
" objects such as \\Box or \\DBox. Scalar types include floats and booleans. @/li\n"
"@li Meta information keys are strings and are supported also for GDS which only accepts integer number "
" keys for user properties. @/li\n"
"@/ul\n"
"\n"
"Elementary (serializable) objects are: \\Box, \\DBox, \\Edge, \\DEdge, \\EdgePair, \\DEdgePair, "
"\\EdgePairs, \\Edges, \\LayerProperties, \\Matrix2d, \\Matrix3d, \\Path, \\DPath, \\Point, \\DPoint, "
"\\Polygon, \\DPolygon, \\SimplePolygon, \\DSimplePolygon, \\Region, \\Text, \\DText, \\Texts, "
"\\Trans, \\DTrans, \\CplxTrans, \\ICplxTrans, \\DCplxTrans, \\VCplxTrans, \\Vector, \\DVector "
"(list may not be complete).\n"
"\n"
"KLayout itself also generates meta information with specific keys. "
"For disambiguation, namespaces can be established by prefixing "
"the key strings with some unique identifier in XML fashion, like a domain name - "
"e.g. 'example.com:key'.\n"
"\n"
"@b Note: @/b only meta information marked with \\is_persisted? == true is stored in GDS or OASIS files. "
"This is not the default setting, so you need to explicitly set that flag.\n"
"\n"
"See also \\Layout#each_meta_info, \\Layout#meta_info_value, \\Layout#meta_info and \\Layout#remove_meta_info as "
"well as the corresponding \\Cell methods.\n"
"\n"
"An example of how to attach persisted meta information to a cell is here:\n"
"\n"
"@code\n"
"ly = RBA::Layout::new\n"
"c1 = ly.create_cell(\"C1\")\n"
"\n"
"mi = RBA::LayoutMetaInfo::new(\"the-answer\", 42.0)\n"
"mi.persisted = true\n"
"c1.add_meta_info(mi)\n"
"\n"
"# will now hold this piece of meta information attached to cell 'C1':\n"
"ly.write(\"to.gds\")\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.25 and was extended in version 0.28.8."
);
}

View File

@ -0,0 +1,100 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2023 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_gsiDeclDbMetaInfo
#define _HDR_gsiDeclDbMetaInfo
#include "dbLayout.h"
#include "tlVariant.h"
#include "tlObject.h"
#include <string>
#include <iterator>
namespace gsi
{
struct MetaInfo
{
MetaInfo (const std::string &n, const std::string &d, const tl::Variant &v, bool p)
: name (n), description (d), value (v), persisted (p)
{ }
MetaInfo (const std::string &n, const db::MetaInfo &mi)
: name (n), description (mi.description), value (mi.value), persisted (mi.persisted)
{ }
MetaInfo ()
: name (), description (), value (), persisted (false)
{ }
std::string name;
std::string description;
tl::Variant value;
bool persisted;
};
struct MetaInfoIterator
{
typedef std::forward_iterator_tag iterator_category;
typedef MetaInfo value_type;
typedef void difference_type;
typedef MetaInfo reference;
typedef void pointer;
MetaInfoIterator ()
: mp_layout (), m_b (), m_e ()
{ }
MetaInfoIterator (const db::Layout *layout, db::Layout::meta_info_iterator b, db::Layout::meta_info_iterator e)
: mp_layout (const_cast<db::Layout *> (layout)), m_b (b), m_e (e)
{ }
bool at_end () const
{
return !mp_layout || m_b == m_e;
}
void operator++ ()
{
if (mp_layout) {
++m_b;
}
}
MetaInfo operator* () const
{
if (mp_layout) {
return MetaInfo (mp_layout->meta_info_name (m_b->first), m_b->second);
} else {
return MetaInfo ();
}
}
private:
tl::weak_ptr<db::Layout> mp_layout;
db::Layout::meta_info_iterator m_b, m_e;
};
}
#endif

View File

@ -66,6 +66,9 @@ public:
void begin_edge_differences ();
void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Edge, db::properties_id_type> > &a, const std::vector <std::pair <db::Edge, db::properties_id_type> > &b);
void end_edge_differences ();
void begin_edge_pair_differences ();
void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &a, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &b);
void end_edge_pair_differences ();
void begin_text_differences ();
void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Text, db::properties_id_type> > &a, const std::vector <std::pair <db::Text, db::properties_id_type> > &b);
void end_text_differences ();
@ -339,6 +342,26 @@ TestDifferenceReceiver::end_edge_differences ()
{
}
void
TestDifferenceReceiver::begin_edge_pair_differences ()
{
m_os << "layout_diff: edge pairs differ for layer " << m_layer.to_string () << " in cell " << m_cellname << std::endl;
}
void
TestDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &a, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &b)
{
m_os << "Not in b but in a:" << std::endl;
print_diffs (pr, a, b);
m_os << "Not in a but in b:" << std::endl;
print_diffs (pr, b, a);
}
void
TestDifferenceReceiver::end_edge_pair_differences ()
{
}
void
TestDifferenceReceiver::begin_text_differences ()
{

View File

@ -543,7 +543,7 @@ TEST(5)
db::Layout l (&m);
EXPECT_EQ (l.technology_name (), "");
db::ProxyContextInfo info;
db::LayoutOrCellContextInfo info;
info.lib_name = "LIB";
info.cell_name = "LIBCELL";
@ -624,7 +624,7 @@ TEST(6)
EXPECT_EQ (l.technology_name (), "");
db::ProxyContextInfo info;
db::LayoutOrCellContextInfo info;
info.lib_name = "Basic";
info.pcell_name = "CIRCLE";
info.pcell_parameters ["actual_radius"] = tl::Variant (10.0);
@ -644,7 +644,7 @@ TEST(6)
EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-4142 -10000} {-10000 -4142} {-10000 4142} {-4142 10000} {4142 10000} {10000 4142} {10000 -4142} {4142 -10000} {-4142 -10000}\nend_cell\nend_lib\n");
db::ProxyContextInfo info2;
db::LayoutOrCellContextInfo info2;
l.get_context_info (cell->cell_index (), info2);
info2.pcell_parameters ["actual_radius"] = tl::Variant (5.0);
@ -677,7 +677,7 @@ TEST(7_LayerProperties)
db::Layout l (&m);
EXPECT_EQ (l.is_valid_layer (0), false);
EXPECT_EQ (l.guiding_shape_layer (), 0);
EXPECT_EQ (l.guiding_shape_layer (), (unsigned int) 0);
EXPECT_EQ (l.is_special_layer (0), true);
EXPECT_EQ (int (l.layers ()), 1);
@ -737,3 +737,61 @@ TEST(7_LayerProperties)
EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (1, 0)), -1);
EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (2, 0)), -1);
}
TEST(8_MetaInfo)
{
db::Layout ly;
EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0);
EXPECT_EQ (ly.meta_info_name_id ("b"), (unsigned int) 1);
EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0);
EXPECT_EQ (ly.has_context_info (), false);
ly.add_meta_info ("a", db::MetaInfo ("description", tl::Variant (17.5), false));
ly.add_meta_info ("b", db::MetaInfo ("", tl::Variant ("value"), true));
EXPECT_EQ (ly.has_context_info (), true);
EXPECT_EQ (ly.meta_info ("x").value.to_string (), "nil");
EXPECT_EQ (ly.meta_info ("x").description, "");
EXPECT_EQ (ly.meta_info ("x").persisted, false);
EXPECT_EQ (ly.meta_info ("a").value.to_string (), "17.5");
EXPECT_EQ (ly.meta_info ("a").description, "description");
EXPECT_EQ (ly.meta_info ("a").persisted, false);
EXPECT_EQ (ly.meta_info (1).value.to_string (), "value");
EXPECT_EQ (ly.meta_info (1).description, "");
EXPECT_EQ (ly.meta_info (1).persisted, true);
db::cell_index_type ci = ly.add_cell ("X");
EXPECT_EQ (ly.has_context_info (ci), false);
ly.add_meta_info (ci, "a", db::MetaInfo ("dd", tl::Variant (-1), false));
ly.add_meta_info (ci, "b", db::MetaInfo ("d", tl::Variant ("u"), true));
EXPECT_EQ (ly.has_context_info (ci), true);
EXPECT_EQ (ly.meta_info (ci, "x").value.to_string (), "nil");
EXPECT_EQ (ly.meta_info (ci, "x").description, "");
EXPECT_EQ (ly.meta_info (ci, "x").persisted, false);
EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "-1");
EXPECT_EQ (ly.meta_info (ci, "a").description, "dd");
EXPECT_EQ (ly.meta_info (ci, "a").persisted, false);
EXPECT_EQ (ly.meta_info (ci, 1).value.to_string (), "u");
EXPECT_EQ (ly.meta_info (ci, 1).description, "d");
EXPECT_EQ (ly.meta_info (ci, 1).persisted, true);
EXPECT_EQ (ly.has_context_info (), true);
ly.clear_meta ();
EXPECT_EQ (ly.has_context_info (), false);
EXPECT_EQ (ly.meta_info ("a").value.to_string (), "nil");
EXPECT_EQ (ly.has_context_info (ci), true);
ly.clear_meta (ci);
EXPECT_EQ (ly.has_context_info (ci), false);
EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "nil");
}

View File

@ -800,7 +800,7 @@ MainService::cm_make_cell_variants ()
if (needs_variant) {
// need to create a variant: create a new cell
db::cell_index_type new_cell_index = layout.add_cell (layout.cell_name (elem.inst_ptr.cell_index ()));
db::cell_index_type new_cell_index = layout.add_cell (layout, elem.inst_ptr.cell_index ());
// prepare a new variant cell
db::Cell &new_cell = layout.cell (new_cell_index);

View File

@ -200,9 +200,10 @@ LibraryController::sync_files ()
reader.read (lib->layout ());
// Use the libname if there is one
db::Layout::meta_info_name_id_type libname_name_id = lib->layout ().meta_info_name_id ("libname");
for (db::Layout::meta_info_iterator m = lib->layout ().begin_meta (); m != lib->layout ().end_meta (); ++m) {
if (m->name == "libname" && ! m->value.empty ()) {
lib->set_name (m->value);
if (m->first == libname_name_id && ! m->second.value.is_nil ()) {
lib->set_name (m->second.value.to_string ());
break;
}
}

View File

@ -3202,6 +3202,25 @@ LayoutViewBase::reload_layout (unsigned int cv_index)
goto_view (state);
}
static void
get_lyp_from_meta_info (const db::Layout &layout, std::string &lyp_file, bool &add_other_layers)
{
db::Layout::meta_info_name_id_type layer_properties_file_name_id = layout.meta_info_name_id ("layer-properties-file");
db::Layout::meta_info_name_id_type layer_properties_add_other_layers_name_id = layout.meta_info_name_id ("layer-properties-add-other-layers");
for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) {
if (meta->first == layer_properties_file_name_id) {
lyp_file = meta->second.value.to_string ();
}
if (meta->first == layer_properties_add_other_layers_name_id) {
try {
add_other_layers = meta->second.value.to_bool ();
} catch (...) {
}
}
}
}
unsigned int
LayoutViewBase::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview, bool initialize_layers)
{
@ -3266,17 +3285,7 @@ LayoutViewBase::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview,
}
// Give the layout object a chance to specify a certain layer property file
for (db::Layout::meta_info_iterator meta = cv->layout ().begin_meta (); meta != cv->layout ().end_meta (); ++meta) {
if (meta->name == "layer-properties-file") {
lyp_file = meta->value;
}
if (meta->name == "layer-properties-add-other-layers") {
try {
tl::from_string (meta->value, add_other_layers);
} catch (...) {
}
}
}
get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers);
// interpolate the layout properties file name
tl::Eval expr;
@ -3438,17 +3447,7 @@ LayoutViewBase::load_layout (const std::string &filename, const db::LoadLayoutOp
}
// Give the layout object a chance to specify a certain layer property file
for (db::Layout::meta_info_iterator meta = cv->layout().begin_meta (); meta != cv->layout().end_meta (); ++meta) {
if (meta->name == "layer-properties-file") {
lyp_file = meta->value;
}
if (meta->name == "layer-properties-add-other-layers") {
try {
tl::from_string (meta->value, add_other_layers);
} catch (...) {
}
}
}
get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers);
// interpolate the layout properties file name
tl::Eval expr;

View File

@ -49,7 +49,7 @@
<item row="1" column="0">
<widget class="QTabWidget" name="mode_tab">
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
@ -186,6 +186,48 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Meta Info</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Meta Info is additional system data shown here for information. Entries marked with a &quot;*&quot; are persisted in the layout file.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="meta_info_list">
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<column>
<property name="text">
<string>Key</string>
</property>
</column>
<column>
<property name="text">
<string>Description</string>
</property>
</column>
<column>
<property name="text">
<string>Value</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>

View File

@ -1186,8 +1186,32 @@ UserPropertiesForm::set_properties (const db::PropertiesRepository::properties_s
mp_ui->text_edit->setPlainText (tl::to_qstring (text));
}
void
UserPropertiesForm::set_meta_info (db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta, const db::Layout &layout)
{
m_begin_meta = begin_meta;
m_end_meta = end_meta;
mp_ui->mode_tab->setTabVisible (2, m_begin_meta != m_end_meta);
mp_ui->meta_info_list->clear ();
for (auto m = m_begin_meta; m != m_end_meta; ++m) {
QTreeWidgetItem *entry = new QTreeWidgetItem (mp_ui->meta_info_list);
entry->setText (0, tl::to_qstring ((m->second.persisted ? "*" : "") + layout.meta_info_name (m->first)));
entry->setText (1, tl::to_qstring (m->second.description));
entry->setText (2, tl::to_qstring (m->second.value.to_parsable_string ()));
}
}
bool
UserPropertiesForm::show (LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id)
{
return show (view, cv_index, prop_id, db::Layout::meta_info_iterator (), db::Layout::meta_info_iterator ());
}
bool
UserPropertiesForm::show (LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id, db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta)
{
bool ret = false;
@ -1209,6 +1233,8 @@ BEGIN_PROTECTED
const db::PropertiesRepository::properties_set &props = mp_prep->properties (prop_id);
set_properties (props);
set_meta_info (begin_meta, end_meta, cv->layout ());
if (exec ()) {
if (m_editable) {

View File

@ -25,10 +25,7 @@
#ifndef HDR_layDialogs
#define HDR_layDialogs
#include "dbPoint.h"
#include "dbVector.h"
#include "dbTypes.h"
#include "dbPropertiesRepository.h"
#include "dbLayout.h"
#include "layuiCommon.h"
#include <QDialog>
@ -36,12 +33,6 @@
class QTreeWidgetItem;
namespace db
{
class Layout;
struct LayerProperties;
}
namespace lay
{
class GenericSyntaxHighlighterAttributes;
@ -431,6 +422,7 @@ public:
virtual ~UserPropertiesForm ();
bool show (lay::LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id);
bool show (lay::LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id, db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta);
public slots:
void add ();
@ -442,11 +434,13 @@ public slots:
private:
db::PropertiesRepository::properties_set get_properties (int tab);
void set_properties (const db::PropertiesRepository::properties_set &props);
void set_meta_info (db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta, const db::Layout &layout);
void accept ();
bool m_editable;
db::PropertiesRepository *mp_prep;
Ui::UserPropertiesForm *mp_ui;
db::Layout::meta_info_iterator m_begin_meta, m_end_meta;
std::unique_ptr<lay::GenericSyntaxHighlighterAttributes> mp_hl_attributes, mp_hl_basic_attributes;
};

View File

@ -154,7 +154,7 @@ LayoutPropertiesForm::prop_pb_clicked ()
db::properties_id_type prop_id = layout.prop_id ();
lay::UserPropertiesForm props_form (this);
if (props_form.show (mp_view, m_index, prop_id)) {
if (props_form.show (mp_view, m_index, prop_id, layout.begin_meta (), layout.end_meta ())) {
mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Edit layout's user properties")));
layout.prop_id (prop_id);

View File

@ -440,7 +440,7 @@ LayoutViewFunctions::cm_cell_user_properties ()
db::properties_id_type prop_id = cell.prop_id ();
lay::UserPropertiesForm props_form (parent_widget ());
if (props_form.show (view (), cv_index, prop_id)) {
if (props_form.show (view (), cv_index, prop_id, layout.begin_meta (cell.cell_index ()), layout.end_meta (cell.cell_index ()))) {
view ()->transaction (tl::to_string (tr ("Edit cell's user properties")));
cell.prop_id (prop_id);

View File

@ -185,8 +185,8 @@ GDS2ReaderBase::do_read (db::Layout &layout)
unsigned int mod_time[6] = { 0, 0, 0, 0, 0, 0 };
unsigned int access_time[6] = { 0, 0, 0, 0, 0, 0 };
get_time (mod_time, access_time);
layout.add_meta_info (MetaInfo ("mod_time", tl::to_string (tr ("Modification Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", mod_time[1], mod_time[2], mod_time[0], mod_time[3], mod_time[4], mod_time[5])));
layout.add_meta_info (MetaInfo ("access_time", tl::to_string (tr ("Access Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", access_time[1], access_time[2], access_time[0], access_time[3], access_time[4], access_time[5])));
layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", mod_time[1], mod_time[2], mod_time[0], mod_time[3], mod_time[4], mod_time[5])));
layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", access_time[1], access_time[2], access_time[0], access_time[3], access_time[4], access_time[5])));
long attr = 0;
db::PropertiesRepository::properties_set layout_properties;
@ -234,9 +234,9 @@ GDS2ReaderBase::do_read (db::Layout &layout)
double dbuu = get_double ();
double dbum = get_double ();
layout.add_meta_info (MetaInfo ("dbuu", tl::to_string (tr ("Database unit in user units")), tl::to_string (dbuu)));
layout.add_meta_info (MetaInfo ("dbum", tl::to_string (tr ("Database unit in meter")), tl::to_string (dbum)));
layout.add_meta_info (MetaInfo ("libname", tl::to_string (tr ("Library name")), m_libname));
layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbuu)));
layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbum)));
layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), m_libname));
m_dbuu = dbuu;
m_dbu = dbum * 1e6; /*in micron*/
@ -289,13 +289,19 @@ GDS2ReaderBase::do_read (db::Layout &layout)
db::cell_index_type cell_index = make_cell (layout, m_cellname);
bool ignore_cell = false;
std::map <tl::string, std::vector <std::string> >::const_iterator ctx = m_context_info.find (m_cellname);
auto ctx = m_context_info.find (m_cellname);
if (ctx != m_context_info.end ()) {
CommonReaderLayerMapping layer_mapping (this, &layout);
if (layout.recover_proxy_as (cell_index, ctx->second.begin (), ctx->second.end (), &layer_mapping)) {
LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ());
if (ci.has_proxy_info () && layout.recover_proxy_as (cell_index, ci, &layer_mapping)) {
// ignore everything in that cell since it is created by the import:
ignore_cell = true;
}
layout.fill_meta_info_from_context (cell_index, ci);
}
db::Cell *cell = 0;
@ -386,6 +392,13 @@ GDS2ReaderBase::do_read (db::Layout &layout)
}
// deserialize global context information
auto ctx = m_context_info.find (std::string ());
if (ctx != m_context_info.end ()) {
LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ());
layout.fill_meta_info_from_context (ci);
}
// check, if the last record is a ENDLIB
if (rec_id != sENDLIB) {
error (tl::to_string (tr ("ENDLIB record expected")));
@ -396,12 +409,16 @@ void
GDS2ReaderBase::read_context_info_cell ()
{
short rec_id = 0;
std::string cn;
// read cell content
while ((rec_id = get_record ()) != sENDSTR) {
progress_checkpoint ();
bool valid_hook = false;
cn.clear ();
if (rec_id == sSREF) {
do {
@ -411,7 +428,7 @@ GDS2ReaderBase::read_context_info_cell ()
error (tl::to_string (tr ("SNAME record expected")));
}
std::string cn = get_string ();
cn = get_string ();
rec_id = get_record ();
while (rec_id == sSTRANS || rec_id == sANGLE || rec_id == sMAG) {
@ -421,6 +438,24 @@ GDS2ReaderBase::read_context_info_cell ()
error (tl::to_string (tr ("XY record expected")));
}
valid_hook = true;
} else if (rec_id == sBOUNDARY) {
rec_id = get_record ();
while (rec_id == sLAYER || rec_id == sDATATYPE) {
rec_id = get_record ();
}
if (rec_id != sXY) {
error (tl::to_string (tr ("XY record expected")));
}
valid_hook = true;
}
if (valid_hook) {
std::vector <std::string> &strings = m_context_info.insert (std::make_pair (cn, std::vector <std::string> ())).first->second;
size_t attr = 0;

View File

@ -72,6 +72,109 @@ inline int scale (double sf, int value)
}
}
void
GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, const std::vector<db::cell_index_type> &cells)
{
write_record_size (4 + 12 * 2);
write_record (sBGNSTR);
write_time (time_data);
write_time (time_data);
write_string_record (sSTRNAME, "$$$CONTEXT_INFO$$$");
std::vector <std::string> context_prop_strings;
if (layout.has_context_info ()) {
// Use a dummy BOUNDARY element to attach the global context
write_record_size (4);
write_record (sBOUNDARY);
write_record_size (6);
write_record (sLAYER);
write_short (0);
write_record_size (6);
write_record (sDATATYPE);
write_short (0);
write_record_size (4 + 5 * 2 * 4);
write_record (sXY);
for (unsigned int i = 0; i < 10; ++i) {
write_int (0);
}
context_prop_strings.clear ();
if (layout.get_context_info (context_prop_strings)) {
// Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings
// will arrive)
for (std::vector <std::string>::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) {
--s;
write_record_size (6);
write_record (sPROPATTR);
write_short (short (std::distance (std::vector <std::string>::const_iterator (context_prop_strings.begin ()), s))); // = user string
write_string_record (sPROPVALUE, *s);
}
}
write_record_size (4);
write_record (sENDEL);
}
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
if (layout.has_context_info (*cell)) {
write_record_size (4);
write_record (sSREF);
write_string_record (sSNAME, m_cell_name_map.cell_name (*cell));
write_record_size (12);
write_record (sXY);
write_int (0);
write_int (0);
context_prop_strings.clear ();
if (layout.get_context_info (*cell, context_prop_strings)) {
// Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings
// will arrive)
for (std::vector <std::string>::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) {
--s;
write_record_size (6);
write_record (sPROPATTR);
write_short (short (std::distance (std::vector <std::string>::const_iterator (context_prop_strings.begin ()), s))); // = user string
write_string_record (sPROPVALUE, *s);
}
}
write_record_size (4);
write_record (sENDEL);
}
}
write_record_size (4);
write_record (sENDSTR);
}
void
GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
{
@ -86,9 +189,9 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
db::GDS2WriterOptions gds2_options = options.get_options<db::GDS2WriterOptions> ();
layout.add_meta_info (MetaInfo ("dbuu", tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units))));
layout.add_meta_info (MetaInfo ("dbum", tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6)));
layout.add_meta_info (MetaInfo ("libname", tl::to_string (tr ("Library name")), gds2_options.libname));
layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units))));
layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6)));
layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), gds2_options.libname));
std::vector <std::pair <unsigned int, db::LayerProperties> > layers;
options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignNumber);
@ -123,8 +226,8 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
}
std::string str_time = tl::sprintf ("%d/%d/%d %d:%02d:%02d", time_data[1], time_data[2], time_data[0], time_data[3], time_data[4], time_data[5]);
layout.add_meta_info (MetaInfo ("mod_time", tl::to_string (tr ("Modification Time")), str_time));
layout.add_meta_info (MetaInfo ("access_time", tl::to_string (tr ("Access Time")), str_time));
layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), str_time));
layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), str_time));
bool multi_xy = gds2_options.multi_xy_records;
size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8);
@ -178,73 +281,17 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
// write context info
bool any_proxy = false;
bool has_context = false;
if (options.write_context_info ()) {
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end () && !any_proxy; ++cell) {
const db::Cell &cref = layout.cell (*cell);
if (cref.is_proxy () && ! cref.is_top ()) {
any_proxy = true;
}
has_context = layout.has_context_info ();
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end () && !has_context; ++cell) {
has_context = layout.has_context_info (*cell);
}
}
if (any_proxy) {
write_record_size (4 + 12 * 2);
write_record (sBGNSTR);
write_time (time_data);
write_time (time_data);
write_string_record (sSTRNAME, "$$$CONTEXT_INFO$$$");
std::vector <std::string> context_prop_strings;
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref = layout.cell (*cell);
if (cref.is_proxy () && ! cref.is_top ()) {
write_record_size (4);
write_record (sSREF);
write_string_record (sSNAME, m_cell_name_map.cell_name (*cell));
write_record_size (12);
write_record (sXY);
write_int (0);
write_int (0);
context_prop_strings.clear ();
if (layout.get_context_info (*cell, context_prop_strings)) {
// Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings
// will arrive)
for (std::vector <std::string>::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) {
--s;
write_record_size (6);
write_record (sPROPATTR);
write_short (short (std::distance (std::vector <std::string>::const_iterator (context_prop_strings.begin ()), s))); // = user string
write_string_record (sPROPVALUE, *s);
}
}
write_record_size (4);
write_record (sENDEL);
}
}
write_record_size (4);
write_record (sENDSTR);
if (has_context) {
write_context_cell (layout, time_data, cells);
}
// body

View File

@ -168,6 +168,7 @@ private:
db::WriterCellNameMap m_cell_name_map;
void write_properties (const db::Layout &layout, db::properties_id_type prop_id);
void write_context_cell (db::Layout &layout, const short *time_data, const std::vector<cell_index_type> &cells);
};
} // namespace db

View File

@ -145,9 +145,10 @@ public:
// Initialize the libname property from meta data with key "libname".
db::GDS2WriterOptions *options = dynamic_cast<db::GDS2WriterOptions *> (o);
if (options) {
db::Layout::meta_info_name_id_type libname_name_id = lh.layout().meta_info_name_id ("libname");
for (db::Layout::meta_info_iterator meta = lh.layout().begin_meta (); meta != lh.layout().end_meta (); ++meta) {
if (meta->name == "libname" && !meta->value.empty ()) {
options->libname = meta->value;
if (meta->first == libname_name_id && !meta->second.value.is_nil ()) {
options->libname = meta->second.value.to_string ();
}
}
}

View File

@ -229,7 +229,7 @@ TEST(1)
db::LayerMap map = reader.read (layout);
EXPECT_EQ (fabs (layout.dbu () / 0.001 - 1.0) < 1e-6, true);
EXPECT_EQ (layout.meta_info_value ("libname"), "LIB.DB");
EXPECT_EQ (layout.meta_info ("libname").value.to_string (), "LIB.DB");
EXPECT_EQ (layout.layers (), size_t (11));
EXPECT_EQ (map.mapping_str (0), "2/0 : 2/0");
@ -289,7 +289,7 @@ TEST(2)
db::Reader reader (file);
map = reader.read (layout_none, options);
EXPECT_EQ (fabs (layout_none.dbu () / 0.001 - 1.0) < 1e-6, true);
EXPECT_EQ (layout.meta_info_value ("libname"), "LIB.DB");
EXPECT_EQ (layout.meta_info ("libname").value.to_string (), "LIB.DB");
}
EXPECT_EQ (layout_none.layers (), size_t (0));

View File

@ -1193,6 +1193,104 @@ TEST(121)
run_test (_this, "t121.oas.gz", "t121_au.gds.gz", true, opt);
}
// Meta info
TEST(130a)
{
db::Layout layout_org;
layout_org.add_cell ("U");
db::cell_index_type ci = layout_org.add_cell ("X");
layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true));
layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true));
layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true));
layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true));
// complex type
tl::Variant v2;
v2.set_array ();
v2.insert ("x", "value_for_x");
v2.insert ("y", db::DBox (1.5, 2.5, 3.5, 4.5));
tl::Variant v1;
v1.set_list (0);
v1.push (-1.5);
v1.push (v2);
layout_org.add_meta_info (ci, "complex", db::MetaInfo ("", v1, true));
layout_org.add_meta_info ("complex", db::MetaInfo ("", v1, true));
std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130a.gds");
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
db::Writer writer (options);
writer.write (layout_org, out);
}
db::Layout layout_read;
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
reader.read (layout_read);
}
EXPECT_EQ (layout_read.has_meta_info ("x"), false);
EXPECT_EQ (layout_read.has_meta_info ("a"), true);
EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5");
EXPECT_EQ (layout_read.meta_info ("a").description, "description");
EXPECT_EQ (layout_read.has_meta_info ("b"), true);
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value");
EXPECT_EQ (layout_read.meta_info ("b").description, "");
EXPECT_EQ (layout_read.has_meta_info ("complex"), true);
EXPECT_EQ (layout_read.meta_info ("complex").value.is_list (), true);
EXPECT_EQ (layout_read.meta_info ("complex").value.size (), size_t (2));
EXPECT_EQ (layout_read.meta_info ("complex").value.begin () [1].is_array (), true);
EXPECT_EQ (layout_read.meta_info ("complex").value.to_string (), "-1.5,x=>value_for_x,y=>(1.5,2.5;3.5,4.5)");
db::cell_index_type ci2 = layout_read.cell_by_name ("X").second;
EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info (ci2, "a").value.to_string (), "true");
EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd");
EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1");
EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d");
EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.is_list (), true);
EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.size (), size_t (2));
EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.begin () [1].is_array (), true);
EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.to_string (), "-1.5,x=>value_for_x,y=>(1.5,2.5;3.5,4.5)");
tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130b.gds");
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
options.set_write_context_info (false);
db::Writer writer (options);
writer.write (layout_org, out);
}
layout_read = db::Layout ();
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
reader.read (layout_read);
}
EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
ci2 = layout_read.cell_by_name ("X").second;
EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
}
// Extreme fracturing by max. points
TEST(166)
{

View File

@ -316,7 +316,7 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl:
error (tl::to_string (tr ("Could not find 'magic' header line - is this a MAGIC file?")));
}
layout.add_meta_info (db::MetaInfo ("lambda", "lambda value (tech scaling)", tl::to_string (m_lambda)));
layout.add_meta_info ("lambda", db::MetaInfo ("lambda value (tech scaling)", tl::to_string (m_lambda)));
bool valid_layer = false;
unsigned int current_layer = 0;
@ -339,11 +339,11 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl:
if (&m_stream == &stream) {
// initial file - store technology
layout.add_meta_info (db::MetaInfo ("magic_technology", tl::to_string (tr ("MAGIC technology string")), m_tech));
layout.add_meta_info ("magic_technology", db::MetaInfo (tl::to_string (tr ("MAGIC technology string")), m_tech));
// propose this is the KLayout technology unless a good one is given
if (! mp_klayout_tech) {
layout.add_meta_info (db::MetaInfo ("technology", tl::to_string (tr ("Technology name")), m_tech));
layout.add_meta_info ("technology", db::MetaInfo (tl::to_string (tr ("Technology name")), m_tech));
}
}
@ -357,7 +357,7 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl:
if (&m_stream == &stream) {
// initial file - store timestamp
layout.add_meta_info (db::MetaInfo ("magic_timestamp", "MAGIC main file timestamp", tl::to_string (ts)));
layout.add_meta_info ("magic_timestamp", db::MetaInfo ("MAGIC main file timestamp", tl::to_string (ts)));
}
ex.expect_end ();

View File

@ -82,11 +82,14 @@ MAGWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
double lambda = m_options.lambda;
if (lambda <= 0.0) {
const std::string &lv = layout.meta_info_value ("lambda");
if (lv.empty ()) {
const tl::Variant &lv = layout.meta_info ("lambda").value;
if (lv.is_nil ()) {
throw tl::Exception (tl::to_string (tr ("No lambda value configured for MAG writer and no 'lambda' metadata present in layout.")));
} else if (lv.is_a_string ()) {
tl::from_string (lv.to_string (), lambda);
} else if (lv.can_convert_to_double ()) {
lambda = lv.to_double ();
}
tl::from_string (lv, lambda);
}
m_sf = layout.dbu () / lambda;

View File

@ -101,7 +101,8 @@ public:
* @brief The constructor
*/
OASISWriterOptions ()
: compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), write_std_properties (1), subst_char ("*")
: compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false),
write_std_properties (1), subst_char ("*"), tables_at_end (false)
{
// .. nothing yet ..
}
@ -166,6 +167,11 @@ public:
*/
std::string subst_char;
/**
* @brief Hidden option, for testing mainly: write tables at end to force forward references
*/
bool tables_at_end;
/**
* @brief Implementation of FormatSpecificWriterOptions
*/

File diff suppressed because it is too large Load Diff

View File

@ -172,6 +172,8 @@ private:
std::map <unsigned long, std::string> m_propstrings;
std::map <unsigned long, std::string> m_propnames;
std::map <db::cell_index_type, std::vector<tl::Variant> > m_context_strings_per_cell;
tl::vector<db::CellInstArray> m_instances;
tl::vector<db::CellInstArrayWithProperties> m_instances_with_props;
@ -210,6 +212,7 @@ private:
void read_properties (db::PropertiesRepository &rep);
void store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special);
std::pair <bool, db::properties_id_type> read_element_properties (db::PropertiesRepository &rep, bool ignore_special);
void replace_forward_references_in_variant (tl::Variant &v);
unsigned char get_byte ()
{

View File

@ -666,9 +666,11 @@ OASISWriter::OASISWriter ()
mp_layout (0),
mp_cell (0),
m_layer (0), m_datatype (0),
m_write_context_info (false),
m_in_cblock (false),
m_propname_id (0),
m_propstring_id (0),
m_textstring_id (0),
m_proptables_written (false),
m_progress (tl::to_string (tr ("Writing OASIS file")), 10000)
{
@ -1150,6 +1152,313 @@ OASISWriter::reset_modal_variables ()
mm_last_value_list.reset ();
}
void
OASISWriter::write_propname_table (size_t &propnames_table_pos, const std::vector<db::cell_index_type> &cells, const db::Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers)
{
// write the property names collected so far in the order of the ID's.
std::vector<std::pair<unsigned long, std::string> > rev_pn;
rev_pn.reserve (m_propnames.size ());
for (auto p = m_propnames.begin (); p != m_propnames.end (); ++p) {
rev_pn.push_back (std::make_pair (p->second, p->first));
}
std::sort (rev_pn.begin (), rev_pn.end ());
for (auto p = rev_pn.begin (); p != rev_pn.end (); ++p) {
tl_assert (p->first == (unsigned long)(p - rev_pn.begin ()));
begin_table (propnames_table_pos);
write_record_id (7);
write_nstring (p->second.c_str ());
}
// collect and write the future property names
std::set <db::properties_id_type> prop_ids_done;
for (auto cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref (layout.cell (*cell));
if (cref.prop_id () != 0) {
begin_table (propnames_table_pos);
emit_propname_def (cref.prop_id ());
}
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (inst->prop_id ());
begin_table (propnames_table_pos);
emit_propname_def (inst->prop_id ());
m_progress.set (mp_stream->pos ());
}
}
for (auto l = layers.begin (); l != layers.end (); ++l) {
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (shape->prop_id ());
begin_table (propnames_table_pos);
emit_propname_def (shape->prop_id ());
m_progress.set (mp_stream->pos ());
}
shape.finish_array ();
}
}
}
// if needed, emit property name required for the PCell or meta info context information
if (m_write_context_info && m_propnames.find (std::string (klayout_context_name)) == m_propnames.end ()) {
bool has_context = false;
for (auto cell = cells.begin (); cell != cells.end () && ! has_context; ++cell) {
LayoutOrCellContextInfo ci;
has_context = layout.has_context_info (*cell) && layout.get_context_info (*cell, ci);
}
if (has_context) {
m_propnames.insert (std::make_pair (std::string (klayout_context_name), m_propname_id++));
begin_table (propnames_table_pos);
write_record_id (7);
write_nstring (klayout_context_name);
}
}
end_table (propnames_table_pos);
}
void
OASISWriter::write_propstring_table (size_t &propstrings_table_pos, const std::vector<db::cell_index_type> &cells, const db::Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers)
{
// write the property strings collected so far in the order of the ID's.
std::vector<std::pair<unsigned long, const std::string *> > rev_ps;
rev_ps.reserve (m_propstrings.size ());
for (auto p = m_propstrings.begin (); p != m_propstrings.end (); ++p) {
rev_ps.push_back (std::make_pair (p->second, &p->first));
}
std::sort (rev_ps.begin (), rev_ps.end ());
tl_assert (rev_ps.size () == size_t (m_propstring_id));
for (auto p = rev_ps.begin (); p != rev_ps.end (); ++p) {
tl_assert (p->first == (unsigned long)(p - rev_ps.begin ()));
begin_table (propstrings_table_pos);
write_record_id (9);
write_bstring (p->second->c_str ());
}
// collect and write the future property strings
std::set <db::properties_id_type> prop_ids_done;
for (auto cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref (layout.cell (*cell));
if (cref.prop_id () != 0 && prop_ids_done.find (cref.prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (cref.prop_id ());
begin_table (propstrings_table_pos);
emit_propstring_def (cref.prop_id ());
}
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (inst->prop_id ());
begin_table (propstrings_table_pos);
emit_propstring_def (inst->prop_id ());
m_progress.set (mp_stream->pos ());
}
}
for (auto l = layers.begin (); l != layers.end (); ++l) {
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (shape->prop_id ());
begin_table (propstrings_table_pos);
emit_propstring_def (shape->prop_id ());
m_progress.set (mp_stream->pos ());
}
shape.finish_array ();
}
}
}
if (m_write_context_info) {
// emit property string id's required for the PCell and meta info context information
std::vector <std::string> context_prop_strings;
for (auto cell = cells.begin (); cell != cells.end (); ++cell) {
m_progress.set (mp_stream->pos ());
context_prop_strings.clear ();
if (layout.has_context_info (*cell) && layout.get_context_info (*cell, context_prop_strings)) {
for (auto c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) {
begin_table (propstrings_table_pos);
write_record_id (9);
write_bstring (c->c_str ());
++m_propstring_id;
}
}
}
}
}
end_table (propstrings_table_pos);
}
void
OASISWriter::write_cellname_table (size_t &cellnames_table_pos, const std::vector<db::cell_index_type> &cells_by_index, const std::map<db::cell_index_type, size_t> *cell_positions, const db::Layout &layout)
{
bool sequential = true;
for (auto cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) {
sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ()));
}
// CELLNAME (implicit or explicit)
for (auto cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) {
begin_table (cellnames_table_pos);
write_record_id (sequential ? 3 : 4);
write_nstring (layout.cell_name (*cell));
if (! sequential) {
write ((unsigned long) *cell);
}
if (m_options.write_std_properties > 1) {
reset_modal_variables ();
// write S_BOUNDING_BOX entries
std::vector<tl::Variant> values;
// TODO: how to set the "depends on external cells" flag?
db::Box bbox = layout.cell (*cell).bbox ();
if (bbox.empty ()) {
// empty box
values.push_back (tl::Variant ((unsigned int) 0x2));
bbox = db::Box (0, 0, 0, 0);
} else {
values.push_back (tl::Variant ((unsigned int) 0x0));
}
values.push_back (tl::Variant (bbox.left ()));
values.push_back (tl::Variant (bbox.bottom ()));
values.push_back (tl::Variant (bbox.width ()));
values.push_back (tl::Variant (bbox.height ()));
write_property_def (s_bounding_box_name, values, true);
// PROPERTY record with S_CELL_OFFSET
if (cell_positions) {
std::map<db::cell_index_type, size_t>::const_iterator pp = cell_positions->find (*cell);
if (pp != cell_positions->end ()) {
write_property_def (s_cell_offset_name, tl::Variant (pp->second), true);
} else {
write_property_def (s_cell_offset_name, tl::Variant (size_t (0)), true);
}
}
}
}
end_table (cellnames_table_pos);
}
void
OASISWriter::write_textstring_table (size_t &textstrings_table_pos, const std::vector<db::cell_index_type> &cells, const db::Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers)
{
// write present text strings
// collect present strings by ID
std::vector<std::pair<unsigned long, const std::string *> > rev_ts;
rev_ts.reserve (m_textstrings.size ());
for (auto p = m_textstrings.begin (); p != m_textstrings.end (); ++p) {
rev_ts.push_back (std::make_pair (p->second, &p->first));
}
std::sort (rev_ts.begin (), rev_ts.end ());
tl_assert (rev_ts.size () == size_t (m_textstring_id));
for (auto t = rev_ts.begin (); t != rev_ts.end (); ++t) {
tl_assert (t->first == (unsigned long)(t - rev_ts.begin ()));
begin_table (textstrings_table_pos);
write_record_id (5);
write_nstring (t->second->c_str ());
}
// collect future test strings
for (auto cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref (layout.cell (*cell));
for (auto l = layers.begin (); l != layers.end (); ++l) {
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Texts));
while (! shape.at_end ()) {
if (m_textstrings.insert (std::make_pair (shape->text_string (), m_textstring_id)).second) {
begin_table (textstrings_table_pos);
write_record_id (5);
write_astring (shape->text_string ());
++m_textstring_id;
m_progress.set (mp_stream->pos ());
}
++shape;
}
}
}
end_table (textstrings_table_pos);
}
void
OASISWriter::write_layername_table (size_t &layernames_table_pos, const std::vector <std::pair <unsigned int, db::LayerProperties> > &layers)
{
for (auto l = layers.begin (); l != layers.end (); ++l) {
if (! l->second.name.empty ()) {
begin_table (layernames_table_pos);
// write mappings to text layer and shape layers
write_record_id (11);
write_nstring (l->second.name.c_str ());
write_byte (3);
write ((unsigned long) l->second.layer);
write_byte (3);
write ((unsigned long) l->second.datatype);
write_record_id (12);
write_nstring (l->second.name.c_str ());
write_byte (3);
write ((unsigned long) l->second.layer);
write_byte (3);
write ((unsigned long) l->second.datatype);
m_progress.set (mp_stream->pos ());
}
}
end_table (layernames_table_pos);
}
static bool must_write_cell (const db::Cell &cref)
{
// Don't write proxy cells which are not employed
@ -1162,6 +1471,7 @@ static bool skip_cell_body (const db::Cell &cref)
return cref.is_ghost_cell () && cref.empty ();
}
void
OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
{
@ -1172,6 +1482,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
m_layer = m_datatype = 0;
m_in_cblock = false;
m_cblock_buffer.clear ();
m_write_context_info = options.write_context_info ();
m_options = options.get_options<OASISWriterOptions> ();
mp_stream = &stream;
@ -1252,6 +1563,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
// We will collect the standard properties here:
m_propstring_id = m_propname_id = 0;
m_textstring_id = 0;
m_proptables_written = false;
std::vector<std::pair<std::string, unsigned int> > init_props;
@ -1303,306 +1615,47 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
write_props (layout.prop_id ());
}
// build property name and value string tables
{
// write the property names collected so far in the order of the ID's.
std::vector<std::pair<unsigned long, std::string> > rev_pn;
rev_pn.reserve (m_propnames.size ());
for (std::map <std::string, unsigned long>::const_iterator p = m_propnames.begin (); p != m_propnames.end (); ++p) {
rev_pn.push_back (std::make_pair (p->second, p->first));
}
std::sort (rev_pn.begin (), rev_pn.end ());
for (std::vector<std::pair<unsigned long, std::string> >::const_iterator p = rev_pn.begin (); p != rev_pn.end (); ++p) {
tl_assert (p->first == (unsigned long)(p - rev_pn.begin ()));
begin_table (propnames_table_pos);
write_record_id (7);
write_nstring (p->second.c_str ());
}
// collect and write the future property names
std::set <db::properties_id_type> prop_ids_done;
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref (layout.cell (*cell));
if (cref.prop_id () != 0) {
begin_table (propnames_table_pos);
emit_propname_def (cref.prop_id ());
}
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (inst->prop_id ());
begin_table (propnames_table_pos);
emit_propname_def (inst->prop_id ());
m_progress.set (mp_stream->pos ());
}
}
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (shape->prop_id ());
begin_table (propnames_table_pos);
emit_propname_def (shape->prop_id ());
m_progress.set (mp_stream->pos ());
}
shape.finish_array ();
}
}
}
if (options.write_context_info ()) {
// emit property name required for the PCell context information
std::vector <std::string> context_prop_strings;
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref (layout.cell (*cell));
if (cref.is_proxy () && ! cref.is_top () && layout.get_context_info (*cell, context_prop_strings)) {
if (m_propnames.insert (std::make_pair (std::string (klayout_context_name), m_propname_id)).second) {
begin_table (propnames_table_pos);
write_record_id (7);
write_nstring (klayout_context_name);
++m_propname_id;
}
break;
}
}
}
end_table (propnames_table_pos);
}
{
// write the property strings collected so far in the order of the ID's.
std::vector<std::pair<unsigned long, std::string> > rev_ps;
rev_ps.reserve (m_propstrings.size ());
for (std::map <std::string, unsigned long>::const_iterator p = m_propstrings.begin (); p != m_propstrings.end (); ++p) {
rev_ps.push_back (std::make_pair (p->second, p->first));
}
std::sort (rev_ps.begin (), rev_ps.end ());
for (std::vector<std::pair<unsigned long, std::string> >::const_iterator p = rev_ps.begin (); p != rev_ps.end (); ++p) {
tl_assert (p->first == (unsigned long)(p - rev_ps.begin ()));
begin_table (propstrings_table_pos);
write_record_id (9);
write_nstring (p->second.c_str ());
}
// collect and write the future property strings
std::set <db::properties_id_type> prop_ids_done;
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref (layout.cell (*cell));
if (cref.prop_id () != 0 && prop_ids_done.find (cref.prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (cref.prop_id ());
begin_table (propnames_table_pos);
emit_propstring_def (cref.prop_id ());
}
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (inst->prop_id ());
begin_table (propstrings_table_pos);
emit_propstring_def (inst->prop_id ());
m_progress.set (mp_stream->pos ());
}
}
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) {
prop_ids_done.insert (shape->prop_id ());
begin_table (propstrings_table_pos);
emit_propstring_def (shape->prop_id ());
m_progress.set (mp_stream->pos ());
}
shape.finish_array ();
}
}
}
if (options.write_context_info ()) {
// emit property string id's required for the PCell context information
std::vector <std::string> context_prop_strings;
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
m_progress.set (mp_stream->pos ());
const db::Cell &cref (layout.cell (*cell));
if (cref.is_proxy () && ! cref.is_top ()) {
context_prop_strings.clear ();
if (layout.get_context_info (*cell, context_prop_strings)) {
for (std::vector <std::string>::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) {
begin_table (propstrings_table_pos);
write_record_id (9);
write_bstring (c->c_str ());
++m_propstring_id;
}
}
}
}
}
}
end_table (propstrings_table_pos);
}
// Now we cannot open new property ID's in strict mode
m_proptables_written = true;
// build cell name table now in non-strict mode (in strict mode it is written at the
// end because then we have the cell positions fo S_CELL_OFFSET)
if (! m_options.strict_mode) {
size_t pos = 0;
bool sequential = true;
for (std::vector<db::cell_index_type>::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) {
sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ()));
}
// CELLNAME (implicit or explicit)
for (std::vector<db::cell_index_type>::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) {
begin_table (pos);
write_record_id (sequential ? 3 : 4);
write_nstring (layout.cell_name (*cell));
if (! sequential) {
write ((unsigned long) *cell);
}
if (m_options.write_std_properties > 1) {
reset_modal_variables ();
// write S_BOUNDING_BOX entries
std::vector<tl::Variant> values;
// TODO: how to set the "depends on external cells" flag?
db::Box bbox = layout.cell (*cell).bbox ();
if (bbox.empty ()) {
// empty box
values.push_back (tl::Variant ((unsigned int) 0x2));
bbox = db::Box (0, 0, 0, 0);
} else {
values.push_back (tl::Variant ((unsigned int) 0x0));
}
values.push_back (tl::Variant (bbox.left ()));
values.push_back (tl::Variant (bbox.bottom ()));
values.push_back (tl::Variant (bbox.width ()));
values.push_back (tl::Variant (bbox.height ()));
write_property_def (s_bounding_box_name, values, true);
}
}
end_table (pos);
}
// build text string table
{
unsigned int id = 0;
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
const db::Cell &cref (layout.cell (*cell));
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Texts));
while (! shape.at_end ()) {
if (m_textstrings.insert (std::make_pair (shape->text_string (), id)).second) {
begin_table (textstrings_table_pos);
write_record_id (5);
write_astring (shape->text_string ());
++id;
m_progress.set (mp_stream->pos ());
}
++shape;
}
}
}
end_table (textstrings_table_pos);
}
// write layernames table
{
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
if (! l->second.name.empty ()) {
begin_table (layernames_table_pos);
// write mappings to text layer and shape layers
write_record_id (11);
write_nstring (l->second.name.c_str ());
write_byte (3);
write ((unsigned long) l->second.layer);
write_byte (3);
write ((unsigned long) l->second.datatype);
write_record_id (12);
write_nstring (l->second.name.c_str ());
write_byte (3);
write ((unsigned long) l->second.layer);
write_byte (3);
write ((unsigned long) l->second.datatype);
m_progress.set (mp_stream->pos ());
}
}
end_table (layernames_table_pos);
}
std::vector <std::string> context_prop_strings;
// write the global layout context information
if (options.write_context_info () && layout.has_context_info () && layout.get_context_info (context_prop_strings)) {
std::vector<tl::Variant> values;
values.reserve (context_prop_strings.size ());
for (auto i = context_prop_strings.begin (); i != context_prop_strings.end (); ++i) {
values.push_back (tl::Variant (*i));
}
write_property_def (klayout_context_name, values, false);
context_prop_strings.clear ();
}
// write the tables
if (! m_options.tables_at_end) {
write_propname_table (propnames_table_pos, cells, layout, layers);
write_propstring_table (propstrings_table_pos, cells, layout, layers);
// Now we cannot open new property ID's in strict mode
m_proptables_written = true;
// build cell name table now in non-strict mode (in strict mode it is written at the
// end because then we have the cell positions fo S_CELL_OFFSET)
if (! m_options.strict_mode) {
write_cellname_table (cellnames_table_pos, cells_by_index, 0, layout);
}
write_textstring_table (textstrings_table_pos, cells, layout, layers);
write_layername_table (layernames_table_pos, layers);
}
// write cells
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
m_progress.set (mp_stream->pos ());
@ -1630,7 +1683,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
}
// context information as property named KLAYOUT_CONTEXT
if (cref.is_proxy () && options.write_context_info ()) {
if (options.write_context_info () && layout.has_context_info (*cell)) {
context_prop_strings.clear ();
@ -1638,17 +1691,29 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
write_record_id (28);
write_byte (char (0xf6));
unsigned long pnid = 0;
std::map <std::string, unsigned long>::const_iterator pni = m_propnames.find (klayout_context_name);
tl_assert (pni != m_propnames.end ());
write (pni->second);
if (pni == m_propnames.end ()) {
pnid = m_propname_id++;
m_propnames.insert (std::make_pair (klayout_context_name, pnid));
} else {
pnid = pni->second;
}
write (pnid);
write ((unsigned long) context_prop_strings.size ());
for (std::vector <std::string>::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
write_byte (14); // b-string by reference number
unsigned long psid = 0;
std::map <std::string, unsigned long>::const_iterator psi = m_propstrings.find (*c);
tl_assert (psi != m_propstrings.end ());
write (psi->second);
if (psi == m_propstrings.end ()) {
psid = m_propstring_id++;
m_propstrings.insert (std::make_pair (*c, psid)).second;
} else {
psid = psi->second;
}
write (psid);
}
mm_last_property_name = klayout_context_name;
@ -1686,66 +1751,31 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
}
// write the tables if at end
if (m_options.tables_at_end) {
// do not consider future items as everything has been collected
std::vector<cell_index_type> no_cells;
std::vector <std::pair <unsigned int, db::LayerProperties> > no_layers;
write_propname_table (propnames_table_pos, no_cells, layout, no_layers);
write_propstring_table (propstrings_table_pos, no_cells, layout, no_layers);
// Now we cannot open new property ID's in strict mode
m_proptables_written = true;
write_textstring_table (textstrings_table_pos, no_cells, layout, no_layers);
// write all layers here
write_layername_table (layernames_table_pos, layers);
}
// write cell table at the end in strict mode (in that mode we need the cell positions
// for the S_CELL_OFFSET properties)
if (m_options.strict_mode) {
bool sequential = true;
for (std::vector<db::cell_index_type>::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) {
sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ()));
}
for (std::vector<db::cell_index_type>::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) {
begin_table (cellnames_table_pos);
// CELLNAME (explicit)
write_record_id (sequential ? 3 : 4);
write_nstring (layout.cell_name (*cell));
if (! sequential) {
write ((unsigned long) *cell);
}
reset_modal_variables ();
if (m_options.write_std_properties > 1) {
// write S_BOUNDING_BOX entries
std::vector<tl::Variant> values;
// TODO: how to set the "depends on external cells" flag?
db::Box bbox = layout.cell (*cell).bbox ();
if (bbox.empty ()) {
// empty box
values.push_back (tl::Variant ((unsigned int) 0x2));
bbox = db::Box (0, 0, 0, 0);
} else {
values.push_back (tl::Variant ((unsigned int) 0x0));
}
values.push_back (tl::Variant (bbox.left ()));
values.push_back (tl::Variant (bbox.bottom ()));
values.push_back (tl::Variant (bbox.width ()));
values.push_back (tl::Variant (bbox.height ()));
write_property_def (s_bounding_box_name, values, true);
}
// PROPERTY record with S_CELL_OFFSET
std::map<db::cell_index_type, size_t>::const_iterator pp = cell_positions.find (*cell);
if (pp != cell_positions.end ()) {
write_property_def (s_cell_offset_name, tl::Variant (pp->second), true);
} else {
write_property_def (s_cell_offset_name, tl::Variant (size_t (0)), true);
}
}
end_table (cellnames_table_pos);
if (m_options.tables_at_end || m_options.strict_mode) {
write_cellname_table (cellnames_table_pos, cells_by_index, &cell_positions, layout);
}
// END record
@ -2341,9 +2371,15 @@ OASISWriter::write (const db::Text &text, db::properties_id_type prop_id, const
m_progress.set (mp_stream->pos ());
db::Trans trans = text.trans ();
unsigned long text_id = 0;
std::map <std::string, unsigned long>::const_iterator ts = m_textstrings.find (text.string ());
tl_assert (ts != m_textstrings.end ());
unsigned long text_id = ts->second;
if (ts == m_textstrings.end ()) {
text_id = m_textstring_id++;
m_textstrings.insert (std::make_pair (text.string (), text_id));
} else {
text_id = ts->second;
}
unsigned char info = 0x20;

View File

@ -203,12 +203,14 @@ private:
const db::Cell *mp_cell;
int m_layer;
int m_datatype;
bool m_write_context_info;
std::vector<db::Vector> m_pointlist;
tl::OutputMemoryStream m_cblock_buffer;
tl::OutputMemoryStream m_cblock_compressed;
bool m_in_cblock;
unsigned long m_propname_id;
unsigned long m_propstring_id;
unsigned long m_textstring_id;
bool m_proptables_written;
std::map <std::string, unsigned long> m_textstrings;
@ -308,6 +310,12 @@ private:
void write_pointlist (const std::vector<db::Vector> &pointlist, bool for_polygons);
void write_inst_with_rep (const db::CellInstArray &inst, db::properties_id_type prop_id, const db::Vector &disp, const db::Repetition &rep);
void write_propname_table (size_t &propnames_table_pos, const std::vector<db::cell_index_type> &cells, const Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers);
void write_propstring_table (size_t &propstrings_table_pos, const std::vector<db::cell_index_type> &cells, const Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers);
void write_cellname_table (size_t &cellnames_table_pos, const std::vector<db::cell_index_type> &cells_by_index, const std::map<cell_index_type, size_t> *cell_positions, const Layout &layout);
void write_textstring_table (size_t &textstrings_table_pos, const std::vector<db::cell_index_type> &cells, const Layout &layout, const std::vector<std::pair<unsigned int, LayerProperties> > &layers);
void write_layername_table (size_t &layernames_table_pos, const std::vector<std::pair<unsigned int, LayerProperties> > &layers);
};
} // namespace db

View File

@ -33,7 +33,7 @@
#include <cstdlib>
void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress)
void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress, bool tables_at_end)
{
{
db::Manager m (false);
@ -60,6 +60,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
db::OASISWriterOptions oasis_options;
oasis_options.write_cblocks = false;
oasis_options.strict_mode = false;
oasis_options.tables_at_end = tables_at_end;
options.set_options (oasis_options);
writer.write (layout, stream, options);
}
@ -115,6 +116,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
db::OASISWriterOptions oasis_options;
oasis_options.write_cblocks = true;
oasis_options.strict_mode = true;
oasis_options.tables_at_end = tables_at_end;
options.set_options (oasis_options);
writer.write (layout, stream, options);
}
@ -164,6 +166,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
db::OASISWriterOptions oasis_options;
oasis_options.write_cblocks = false;
oasis_options.strict_mode = false;
oasis_options.tables_at_end = tables_at_end;
oasis_options.write_std_properties = 2;
options.set_options (oasis_options);
writer.write (layout, stream, options);
@ -214,6 +217,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
db::OASISWriterOptions oasis_options;
oasis_options.write_cblocks = true;
oasis_options.strict_mode = true;
oasis_options.tables_at_end = tables_at_end;
oasis_options.write_std_properties = 2;
options.set_options (oasis_options);
writer.write (layout, stream, options);
@ -255,6 +259,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
db::OASISWriterOptions oasis_options;
oasis_options.compression_level = compr;
oasis_options.recompress = recompress;
oasis_options.tables_at_end = tables_at_end;
options.set_options (oasis_options);
options.set_scale_factor (3.0);
options.set_dbu (0.0005);
@ -308,11 +313,14 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com
void run_test (tl::TestBase *_this, const char *file, bool scaling_test = true)
{
for (int recompress = 0; recompress < 2; ++recompress) {
run_test (_this, file, scaling_test, 0, recompress);
run_test (_this, file, scaling_test, 1, recompress);
run_test (_this, file, scaling_test, 2, recompress);
run_test (_this, file, scaling_test, 10, recompress);
run_test (_this, file, scaling_test, 0, recompress, false);
run_test (_this, file, scaling_test, 1, recompress, false);
run_test (_this, file, scaling_test, 2, recompress, false);
run_test (_this, file, scaling_test, 10, recompress, false);
}
// tables at end
run_test (_this, file, scaling_test, 2, false, true);
}
TEST(1)
@ -1860,3 +1868,111 @@ TEST(120_IrregularInstRepetitions)
}
}
// Meta info
static void
run_test130 (tl::TestBase *_this, bool strict, bool tables_at_end)
{
db::Layout layout_org;
layout_org.add_cell ("U");
db::cell_index_type ci = layout_org.add_cell ("X");
layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true));
layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true));
layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true));
layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true));
std::string tmp_file = _this->tmp_file ("tmp_OASISWriter1.oas");
{
tl::OutputStream out (tmp_file);
db::OASISWriterOptions oasis_options;
oasis_options.strict_mode = strict;
oasis_options.tables_at_end = tables_at_end;
db::SaveLayoutOptions options;
options.set_format ("OASIS");
options.set_options (oasis_options);
db::Writer writer (options);
writer.write (layout_org, out);
}
db::Layout layout_read;
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
reader.read (layout_read);
}
EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5");
EXPECT_EQ (layout_read.meta_info ("a").description, "description");
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value");
EXPECT_EQ (layout_read.meta_info ("b").description, "");
db::cell_index_type ci2 = layout_read.cell_by_name ("X").second;
EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info (ci2, "a").value.to_string (), "true");
EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd");
EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1");
EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d");
tmp_file = _this->tmp_file ("tmp_OASISWriter2.oas");
{
tl::OutputStream out (tmp_file);
db::OASISWriterOptions oasis_options;
oasis_options.strict_mode = strict;
oasis_options.tables_at_end = tables_at_end;
db::SaveLayoutOptions options;
options.set_format ("OASIS");
options.set_options (oasis_options);
options.set_write_context_info (false);
db::Writer writer (options);
writer.write (layout_org, out);
}
layout_read = db::Layout ();
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
reader.read (layout_read);
}
EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
ci2 = layout_read.cell_by_name ("X").second;
EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
}
// Meta info
TEST(130a)
{
run_test130 (_this, false, false);
}
TEST(130b)
{
run_test130 (_this, true, false);
}
TEST(130c)
{
run_test130 (_this, false, true);
}
TEST(130d)
{
run_test130 (_this, true, true);
}

View File

@ -1146,7 +1146,7 @@ public:
std::string lyr_file = data.get_layer_properties_file ();
if (! lyr_file.empty ()) {
layout.add_meta_info (db::MetaInfo ("layer-properties-file", "Layer Properties File", lyr_file));
layout.add_meta_info ("layer-properties-file", db::MetaInfo ("Layer Properties File", lyr_file));
}
return m_layers;

View File

@ -1044,36 +1044,39 @@ class DBLayoutTests2_TestClass < TestBase
end
# Meta information
def test_12
def test_12a
mi = RBA::LayoutMetaInfo::new("myinfo", "a")
assert_equal(mi.name, "myinfo")
assert_equal(mi.description, "")
assert_equal(mi.value, "a")
assert_equal(mi.is_persisted?, false)
mi.name = "x"
mi.description = "y"
mi.value = "z"
mi.persisted = true
assert_equal(mi.name, "x")
assert_equal(mi.description, "y")
assert_equal(mi.value, "z")
assert_equal(mi.is_persisted?, true)
ly = RBA::Layout::new
ly.add_meta_info(RBA::LayoutMetaInfo::new("myinfo", "a"))
ly.add_meta_info(RBA::LayoutMetaInfo::new("another", "42", "description"))
ly.add_meta_info(RBA::LayoutMetaInfo::new("another", 42, "description"))
assert_equal(ly.meta_info_value("myinfo"), "a")
assert_equal(ly.meta_info_value("doesnotexist"), "")
assert_equal(ly.meta_info_value("another"), "42")
assert_equal(ly.meta_info_value("doesnotexist"), nil)
assert_equal(ly.meta_info_value("another"), 42)
a = []
ly.each_meta_info { |mi| a << mi.name }
assert_equal(a.join(","), "myinfo,another")
a = []
ly.each_meta_info { |mi| a << mi.value }
ly.each_meta_info { |mi| a << mi.value.to_s }
assert_equal(a.join(","), "a,42")
a = []
ly.each_meta_info { |mi| a << mi.description }
@ -1081,13 +1084,64 @@ class DBLayoutTests2_TestClass < TestBase
ly.add_meta_info(RBA::LayoutMetaInfo::new("myinfo", "b"))
assert_equal(ly.meta_info_value("myinfo"), "b")
assert_equal(ly.meta_info_value("doesnotexist"), "")
assert_equal(ly.meta_info_value("another"), "42")
assert_equal(ly.meta_info_value("doesnotexist"), nil)
assert_equal(ly.meta_info_value("another"), 42)
ly.remove_meta_info("doesnotexist") # should not fail
ly.remove_meta_info("myinfo")
assert_equal(ly.meta_info_value("myinfo"), "")
assert_equal(ly.meta_info_value("doesnotexist"), "")
assert_equal(ly.meta_info_value("another"), "42")
assert_equal(ly.meta_info_value("myinfo"), nil)
assert_equal(ly.meta_info_value("doesnotexist"), nil)
assert_equal(ly.meta_info_value("another"), 42)
assert_equal(ly.meta_info("doesnotexist"), nil)
assert_equal(ly.meta_info("another").value, 42)
assert_equal(ly.meta_info("another").description, "description")
ly.clear_meta_info
assert_equal(ly.meta_info_value("another"), nil)
assert_equal(ly.meta_info("another"), nil)
# cellwise
c1 = ly.create_cell("X")
c2 = ly.create_cell("U")
c1.add_meta_info(RBA::LayoutMetaInfo::new("a", true))
c1.add_meta_info(RBA::LayoutMetaInfo::new("b", [ 1, 17, 42 ]))
assert_equal(c2.meta_info("a"), nil)
assert_equal(c2.meta_info_value("a"), nil)
a = []
c2.each_meta_info { |mi| a << mi.value.to_s }
assert_equal(a.join(","), "")
assert_equal(c1.meta_info("a").value, true)
assert_equal(c1.meta_info("b").value, [ 1, 17, 42 ])
assert_equal(c1.meta_info_value("b"), [ 1, 17, 42 ])
a = []
c1.each_meta_info { |mi| a << mi.value.to_s }
assert_equal(a.join(","), "true,[1, 17, 42]")
c1.remove_meta_info("doesnotexist") # should not fail
a = []
c1.each_meta_info { |mi| a << mi.value.to_s }
assert_equal(a.join(","), "true,[1, 17, 42]")
c1.remove_meta_info("b")
a = []
c1.each_meta_info { |mi| a << mi.value.to_s }
assert_equal(a.join(","), "true")
c1.clear_meta_info
a = []
c1.each_meta_info { |mi| a << mi.value.to_s }
assert_equal(a.join(","), "")
end