klayout/src/plugins/streamers/lstream/db_plugin/lstrWriter.cc

1405 lines
43 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2026 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "lstrWriter.h"
#include "lstrPlugin.h"
#include "lstrCompressor.h"
#include "lstrCompressed.h"
#include "lstrFormat.h"
#include "dbLibraryManager.h"
#include "dbLibraryProxy.h"
#include "dbLibrary.h"
#include "tlStream.h"
#include "tlAssert.h"
#include "tlException.h"
#include "tlEnv.h"
#include "metaDataView.capnp.h"
#include <capnp/message.h>
#include <kj/io.h>
// Enable to replicate the messages into separate files for dumping
// and inspection with "capnp decode".
// Env var: $KLAYOUT_LSTREAM_REPLICATE_MESSAGES
static bool s_replicate_messages = tl::app_flag ("lstream-replicate-messages");
namespace lstr
{
class OutputStream
: public kj::OutputStream
{
public:
OutputStream (tl::OutputStream *os)
: mp_os (os)
{
// .. nothing yet ..
}
virtual void write (const void* buffer, size_t size)
{
mp_os->put ((const char *) buffer, size);
}
private:
tl::OutputStream *mp_os;
};
// ------------------------------------------------------------------
// LStreamWriter implementation
Writer::Writer ()
: mp_stream (0), m_progress (tl::to_string (tr ("Writing LStream file")), 1), mp_layout (0)
{
m_progress.set_format (tl::to_string (tr ("%.0f MB")));
m_progress.set_unit (1024 * 1024);
m_recompress = true;
m_compression_level = 2;
m_permissive = true;
}
void
Writer::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
{
// TODO: this seems to be needed to properly enumerate the properties in "collect_property_ids"
layout.update ();
auto lstr_options = options.get_options<lstr::WriterOptions> ();
m_permissive = lstr_options.permissive;
m_compression_level = lstr_options.compression_level;
m_recompress = lstr_options.recompress;
double dbu = (options.dbu () == 0.0) ? layout.dbu () : options.dbu ();
double sf = options.scale_factor () * (layout.dbu () / dbu);
if (fabs (sf - 1.0) < 1e-9) {
// to avoid rounding problems, set to 1.0 exactly if possible.
sf = 1.0;
}
// TODO: implement
if (sf != 1.0) {
throw tl::Exception (tl::to_string (tr ("Scaling is not supported in LStream writer currently")));
}
mp_stream = &stream;
m_options = options;
mp_layout = &layout;
m_cellname.clear ();
m_layout_view_id = -1;
m_meta_data_view_id = -1;
m_layers_to_write.clear ();
#if KLAYOUT_MAJOR_VERSION == 0 && (KLAYOUT_MINOR_VERSION < 30 || (KLAYOUT_MINOR_VERSION == 30 && KLAYOUT_TINY_VERSION < 5))
{
options.get_valid_layers (layout, m_layers_to_write, db::SaveLayoutOptions::LP_OnlyNumbered);
options.get_valid_layers (layout, m_layers_to_write, db::SaveLayoutOptions::LP_OnlyNamed);
// clean up layer duplicates - see TODO above
auto lw = m_layers_to_write.begin ();
std::set<unsigned int> lseen;
for (auto l = m_layers_to_write.begin (); l != m_layers_to_write.end (); ++l) {
if (lseen.find (l->first) == lseen.end ()) {
*lw++ = *l;
lseen.insert (l->first);
}
}
m_layers_to_write.erase (lw, m_layers_to_write.end ());
}
#else
options.get_valid_layers (layout, m_layers_to_write, db::SaveLayoutOptions::LP_AsIs);
#endif
m_cells_to_write.clear ();
options.get_cells (layout, m_cells_to_write, m_layers_to_write);
lstr::OutputStream os_adaptor (&stream);
kj::BufferedOutputStreamWrapper kj_stream (os_adaptor);
// prepare the stream by writing the signature
kj_stream.write (LStream_sig, strlen (LStream_sig) + 1);
// creates the global header
write_header (kj_stream);
// this stream contains a single library currently
write_library (kj_stream);
for (auto c = mp_layout->begin_top_down (); c != mp_layout->end_top_down (); ++c) {
if (m_cells_to_write.find (*c) != m_cells_to_write.end ()) {
m_cellname = layout.cell_name (*c);
write_cell (*c, kj_stream);
m_cellname.clear ();
}
}
}
/**
* @brief Replicates a single message to a separate file for debugging
*
* Decoding a message is easer when it is written to a separate file.
*/
void
Writer::replicate_message (const std::string &suffix, capnp::MessageBuilder &message)
{
if (s_replicate_messages) {
tl::OutputStream os_msg (mp_stream->path () + suffix);
lstr::OutputStream ls_os_msg (&os_msg);
kj::BufferedOutputStreamWrapper kj_os_msg (ls_os_msg);
writePackedMessage (kj_os_msg, message);
}
}
/**
* @brief Is called "frequently" to report the progress
*/
void
Writer::yield_progress ()
{
m_progress.set (mp_stream->pos ());
}
/**
* @brief Issues a warning on the writer
*
* With "m_permissive" set to false, every warning will become an error.
*/
void
Writer::warn (const std::string &msg)
{
std::string msg_full = msg;
if (! m_cellname.empty ()) {
msg_full += tl::to_string (tr (", in cell: "));
msg_full += m_cellname;
}
if (m_permissive) {
tl::warn << msg_full;
} else {
throw tl::Exception (msg_full);
}
}
/**
* @brief Writes the header message to the stream
*
* This is the main header provided by the LStream.
* It lists the libraries available inside this stream.
* For KLayout, this is only one library - of "layout"
* type.
*/
void
Writer::write_header (kj::BufferedOutputStream &os)
{
capnp::MallocMessageBuilder message;
stream::header::Header::Builder header = message.initRoot<stream::header::Header> ();
header.setGenerator (LStream_generator);
header.setTechnology (mp_layout->technology_name ());
header.initLibraries (1);
auto lib = header.getLibraries () [0];
// TODO: use layout's lib name?
lib.setName ("");
lib.setType ("layout");
// NOTE: our layout's metadata is placed in the library
// Write message to stream
writePackedMessage (os, message);
yield_progress ();
replicate_message (".header", message);
}
/**
* @brief Writes the library header message to the stream
*
* This involves build a number of tables before they are written.
* As these tables are the basis for LStream Id allocation, this
* method must be called before LStream Ids can be obtained.
*/
void
Writer::write_library (kj::BufferedOutputStream &os)
{
capnp::MallocMessageBuilder message;
stream::library::Library::Builder library = message.initRoot<stream::library::Library> ();
// Library references
make_library_refs_table (library.getLibraryRefs ());
// Properties
{
std::vector<db::properties_id_type> prop_ids;
std::vector<db::property_names_id_type> prop_names;
collect_property_ids (prop_ids, prop_names);
make_property_names_tables (prop_names, library.getPropertyNamesTable ());
make_properties_tables (prop_ids, library.getPropertiesTable ());
}
// Text strings
{
std::vector<std::string> text_strings;
collect_text_strings (text_strings);
make_text_strings_table (text_strings, library.getTextStringsTable ());
}
// View specs table
// NOTE: currently there are only "layout" views and optionally "metaData" views
{
m_layout_view_id = 0;
m_meta_data_view_id = -1;
bool needs_meta_data_view = false;
for (auto c = m_cells_to_write.begin (); c != m_cells_to_write.end () && !needs_meta_data_view; ++c) {
needs_meta_data_view = (mp_layout->begin_meta (*c) != mp_layout->end_meta (*c));
}
auto view_specs = library.getViewSpecsTable ();
view_specs.initViewSpecs (needs_meta_data_view ? 2 : 1);
auto layout_view = view_specs.getViewSpecs () [m_layout_view_id];
layout_view.setName ("layout");
layout_view.setClass ("LayoutView");
layout_view.setPropertySetId (get_property_id (mp_layout->prop_id ()));
// Computes the resolution:
// Rounds to integer if "close to one". This achieves a
// kind of normalization and prevents propagation of rounding
// errors.
double resolution = 1.0 / mp_layout->dbu ();
double integer_resolution = floor (resolution + 0.5);
if (std::abs (resolution - integer_resolution) < 1e-10) {
resolution = integer_resolution;
}
layout_view.setResolution (integer_resolution);
make_meta_data (0, layout_view.getMetaData ());
// adds a meta data view if needed
if (needs_meta_data_view) {
m_meta_data_view_id = 1;
auto meta_data_view = view_specs.getViewSpecs () [m_meta_data_view_id];
meta_data_view.setName ("metaData");
meta_data_view.setClass ("MetaDataView");
}
}
// Layer table
{
auto view_specs = library.getViewSpecsTable ();
auto layout_view = view_specs.getViewSpecs () [m_layout_view_id];
make_layer_table (layout_view.getLayerTable ());
}
// Cell specs table
make_cell_specs (library.getCellSpecsTable ());
// Cell hierachy tree
make_cell_hierarchy_tree (library.getCellHierarchyTree ());
// Write message to stream
writePackedMessage (os, message);
yield_progress ();
replicate_message (".library", message);
}
/**
* @brief Produces a variant value to a stream::variant::Variant struct
*/
void
Writer::make_variant_value (const tl::Variant &value, stream::variant::Variant::Builder builder)
{
if (value.is_nil ()) {
builder.getValue ().setNil ();
} else if (value.is_bool ()) {
builder.getValue ().setBool (value.to_bool ());
} else if (value.is_a_string ()) {
builder.getValue ().setText (value.to_string ());
} else if (value.can_convert_to_ulonglong ()) {
builder.getValue ().setUint64 (uint64_t (value.to_ulonglong ()));
} else if (value.can_convert_to_longlong ()) {
builder.getValue ().setInt64 (int64_t (value.to_longlong ()));
} else if (value.can_convert_to_double ()) {
builder.getValue ().setDouble (value.to_double ());
} else if (value.is_user ()) {
// NOTE: the "klayout:" prefix indicates the object is in KLayout's
// object serialization notation.
builder.getValue ().setObject (std::string ("klayout:") + value.to_parsable_string ());
} else if (value.is_list ()) {
builder.getValue ().initList (value.size ());
size_t index = 0;
for (auto i = value.begin (); i != value.end (); ++i, ++index) {
make_variant_value (*i, builder.getValue ().getList ()[index]);
}
} else if (value.is_array ()) {
builder.getValue ().initArray (value.array_size ());
size_t index = 0;
for (auto i = value.begin_array (); i != value.end_array (); ++i, ++index) {
make_variant_value (i->first, builder.getValue ().getArray ()[index].getKey ());
make_variant_value (i->second, builder.getValue ().getArray ()[index].getValue ());
}
}
}
/**
* @brief Produces the library names table
*
* This method collects the library references from all cells to write and
* produces a table with all these references. It will also assign
* library name Ids in that step.
*/
void
Writer::make_library_refs_table (stream::library::LibraryRefs::Builder library_refs)
{
m_ls_lib_ids.clear ();
std::vector<std::string> lib_names;
for (auto c = m_cells_to_write.begin (); c != m_cells_to_write.end (); ++c) {
const db::Cell &cell = mp_layout->cell (*c);
const db::LibraryProxy *lib_proxy = dynamic_cast<const db::LibraryProxy *> (&cell);
if (lib_proxy) {
auto lib_id = lib_proxy->lib_id ();
if (m_ls_lib_ids.find (lib_id) == m_ls_lib_ids.end ()) {
const db::Library *lib = db::LibraryManager::instance ().lib (lib_id);
lib_names.push_back (lib->get_name ());
m_ls_lib_ids.insert (std::make_pair (lib_id, lib_names.size ()));
}
}
}
library_refs.initRefs (lib_names.size ());
for (auto n = lib_names.begin (); n != lib_names.end (); ++n) {
auto ref = library_refs.getRefs ()[n - lib_names.begin ()];
ref.setLibraryName (*n);
}
}
/**
* @brief Gets the library name Id from a given library Id
*
* Note that "make_library_names_table" must have been called before
* this method can be used.
*/
uint64_t
Writer::get_library_ref_id (db::lib_id_type lib_id)
{
auto i = m_ls_lib_ids.find (lib_id);
tl_assert (i != m_ls_lib_ids.end ());
return i->second;
}
/**
* @brief Collect all KLayout property name Ids and properties Ids used in the context of this writer
*
* This method scans layout, cells, meta info, instances and shapes for used properties
* and registers properties Ids and names.
*
* @param prop_ids Where the properties Ids are collected
* @param prop_names Where the property names are collected
*/
void
Writer::collect_property_ids (std::vector<db::properties_id_type> &prop_ids, std::vector<db::property_names_id_type> &prop_names)
{
make_property_id (mp_layout->prop_id (), prop_ids, prop_names);
for (auto c = m_cells_to_write.begin (); c != m_cells_to_write.end (); ++c) {
const db::Cell &cell = mp_layout->cell (*c);
// PCell parameters only employ the name ID space
auto param_dict = mp_layout->get_named_pcell_parameters (*c);
for (auto p = param_dict.begin (); p != param_dict.end (); ++p) {
make_property_name_id_from_variant (tl::Variant (p->first), prop_names);
}
make_property_id (cell.prop_id (), prop_ids, prop_names);
for (auto l = m_layers_to_write.begin (); l != m_layers_to_write.end (); ++l) {
for (auto s = db::ShapeIterator (cell.shapes (l->first), db::ShapeIterator::AllWithProperties); ! s.at_end (); s.finish_array ()) {
make_property_id (s->prop_id (), prop_ids, prop_names);
}
}
for (auto i = cell.begin (); ! i.at_end (); ++i) {
make_property_id (i->prop_id (), prop_ids, prop_names);
}
}
}
namespace
{
struct ComparePropertyNameIdByValue
{
bool operator() (db::property_names_id_type a, db::property_names_id_type b) const
{
const tl::Variant &na = db::property_name (a);
const tl::Variant &nb = db::property_name (b);
return na.less (nb);
}
};
}
/**
* @brief Gets the LStream property set Id from a KLayout properties Id
*
* If the properties Id is not registered yet, a new LStream Id will be generated.
*
* @param id The KLayout properties Id
* @param prop_id New KLayout properties Ids will be added here
* @param prop_names New property names go here (happens when a new property set is registered)
*/
uint64_t
Writer::make_property_id (db::properties_id_type id, std::vector<db::properties_id_type> &prop_ids, std::vector<db::property_names_id_type> &prop_names)
{
if (id != 0) {
auto i = m_ls_prop_ids.find (id);
if (i != m_ls_prop_ids.end ()) {
return i->second;
} else {
uint64_t ls_id = uint64_t (prop_ids.size () + 1);
m_ls_prop_ids.insert (std::make_pair (id, ls_id));
prop_ids.push_back (id);
auto ps = db::properties (id);
std::set<db::property_names_id_type, ComparePropertyNameIdByValue> ps_sorted;
for (auto i = ps.begin (); i != ps.end (); ++i) {
ps_sorted.insert (i->first);
}
for (auto i = ps_sorted.begin (); i != ps_sorted.end (); ++i) {
make_property_name_id_from_id (*i, prop_names);
}
return ls_id;
}
} else {
return 0;
}
}
/**
* @brief Gets the LStream property name Id for a given name (by variant)
*
* @param name The name to register
* @param prop_names New names will be added here
*/
uint64_t
Writer::make_property_name_id_from_variant (const tl::Variant &name, std::vector<db::property_names_id_type> &prop_names)
{
return make_property_name_id_from_id (db::property_names_id (name), prop_names);
}
/**
* @brief Gets the LStream property name Id for a given name (by KLayout property name Id)
*
* @param name_id The name Id to register
* @param prop_names New names will be added here
*/
uint64_t
Writer::make_property_name_id_from_id (db::property_names_id_type name_id, std::vector<db::property_names_id_type> &prop_names)
{
auto i = m_ls_prop_name_ids.find (name_id);
if (i != m_ls_prop_name_ids.end ()) {
return i->second;
} else {
uint64_t ls_name_id = prop_names.size ();
prop_names.push_back (name_id);
m_ls_prop_name_ids.insert (std::make_pair (name_id, ls_name_id));
return ls_name_id;
}
}
/**
* @brief Gets the LStream property set Id from a KLayout properties Id
*
* If the properties Id is not valid, this method will assert.
* Note, that "make_property_id" must have been called before to register
* the property set.
*/
uint64_t
Writer::get_property_id (db::properties_id_type id)
{
if (id == 0) {
return 0;
} else {
auto i = m_ls_prop_ids.find (id);
tl_assert (i != m_ls_prop_ids.end ());
return i->second;
}
}
/**
* @brief Obtain the LStream property name Id from KLayout property name Id
*
* If the KLayout property name Id is not valid, this method asserts.
* Note that "make_property_name_id_from_id" must have been called before to
* register the name.
*/
uint64_t
Writer::get_property_name_id_from_id (db::property_names_id_type name_id)
{
auto i = m_ls_prop_name_ids.find (name_id);
tl_assert (i != m_ls_prop_name_ids.end ());
return i->second;
}
/**
* @brief Obtain the LStream property name Id from a name variant
*
* If the name variant is not a valid name, this method asserts.
*/
uint64_t
Writer::get_property_name_id_from_variant (const tl::Variant &name)
{
return get_property_name_id_from_id (db::property_names_id (name));
}
/**
* @brief Produces the property names table from a given set of KLayout property name Ids
*/
void
Writer::make_property_names_tables (const std::vector<db::property_names_id_type> &prop_names, stream::library::PropertyNamesTable::Builder property_names)
{
property_names.initNames (prop_names.size ());
for (auto i = prop_names.begin (); i != prop_names.end (); ++i) {
stream::propertySet::PropertyName::Builder property_name = property_names.getNames ()[i - prop_names.begin ()];
// No namespace yet: property_name.setNamespaceId (0);
make_variant_value (db::property_name (*i), property_name.getName ());
}
}
/**
* @brief Produces the property sets table from a given set of KLayout properties Ids
*
* Note that the property names must have been collected already before this
* method can be used ("make_property_name_id_from_id").
*/
void
Writer::make_properties_tables (const std::vector<db::properties_id_type> &prop_ids, stream::library::PropertiesTable::Builder properties)
{
properties.initPropertySets (prop_ids.size ());
for (auto p = prop_ids.begin (); p != prop_ids.end (); ++p) {
auto set = properties.getPropertySets ()[p - prop_ids.begin ()];
// NOTE: we go through the map to become independent from the name order
auto map = db::properties (*p).to_map ();
set.initProperties (map.size ());
size_t index = 0;
for (auto m = map.begin (); m != map.end (); ++m, ++index) {
auto ni = m_ls_prop_name_ids.find (db::property_names_id (m->first));
tl_assert (ni != m_ls_prop_name_ids.end ());
auto prop = set.getProperties ()[index];
prop.setNameId (ni->second);
make_variant_value (m->second, prop.getValue ());
}
}
}
/**
* @brief Collects all used text strings
*/
void
Writer::collect_text_strings (std::vector<std::string> &text_strings)
{
for (auto c = m_cells_to_write.begin (); c != m_cells_to_write.end (); ++c) {
const db::Cell &cell = mp_layout->cell (*c);
for (auto l = m_layers_to_write.begin (); l != m_layers_to_write.end (); ++l) {
for (auto s = db::ShapeIterator (cell.shapes (l->first), db::ShapeIterator::Texts); ! s.at_end (); s.finish_array ()) {
make_text_string_id (std::string (s->text_string ()), text_strings);
}
}
}
}
/**
* @brief Gets the LStream text string Id for a given text
*
* This method will create a new entry if the string had not been
* registered before.
*
* @param string The new text string
* @param text_strings New strings are added here
*/
uint64_t
Writer::make_text_string_id (const std::string &string, std::vector<std::string> &text_strings)
{
auto i = m_text_strings.find (string);
if (i == m_text_strings.end ()) {
uint64_t id = text_strings.size ();
text_strings.push_back (string);
m_text_strings.insert (std::make_pair (string, id));
return id;
} else {
return i->second;
}
}
/**
* @brief Gets the LStream text string Id for a given text
*
* Note that make_text_string_id had to be called before.
* This method will assert if the string does not have an associated
* text string Id.
*/
uint64_t
Writer::get_text_string_id (const std::string &string)
{
auto i = m_text_strings.find (string);
tl_assert (i != m_text_strings.end ());
return i->second;
}
/**
* @brief Produces the text strings table on stream::library::TextStringsTable
*
* @param text_strings The list of text strings, where the LStream text string Id in an index in that table
* @param table The Builder for the table
*/
void
Writer::make_text_strings_table (const std::vector<std::string> &text_strings, stream::library::TextStringsTable::Builder table)
{
table.initTextStrings (text_strings.size ());
for (auto i = text_strings.begin (); i != text_strings.end (); ++i) {
table.getTextStrings ().set (i - text_strings.begin (), i->c_str ());
}
}
/**
* @brief Produces the layer table on stream::library::LayerTable
*
* Only selected layers will be produced.
*/
void
Writer::make_layer_table (stream::library::LayerTable::Builder layers)
{
layers.initLayerEntries (m_layers_to_write.size ());
for (auto l = m_layers_to_write.begin (); l != m_layers_to_write.end (); ++l) {
auto lp = l->second;
// NOTE: currently, the purpose is always DRAWING
auto le = layers.getLayerEntries ()[l - m_layers_to_write.begin ()];
if (lp.layer >= 0 && lp.datatype >= 0) {
le.initLayerNumbers (2);
le.getLayerNumbers ().set (0, lp.layer);
le.getLayerNumbers ().set (1, lp.datatype);
}
le.setName (lp.name);
le.setPurpose (stream::library::LayerEntry::Purpose::DRAWING);
}
}
/**
* @brief Produces the cell specifications on stream::library::CellSpecsTable
*
* Only selected cells will be produced. The cell specs are generated top-down.
*/
void
Writer::make_cell_specs (stream::library::CellSpecsTable::Builder cell_specs)
{
cell_specs.initCellSpecs (m_cells_to_write.size ());
size_t index = 0;
m_ls_cell_ids.clear ();
for (auto c = mp_layout->begin_top_down (); c != mp_layout->end_top_down (); ++c) {
if (m_cells_to_write.find (*c) == m_cells_to_write.end ()) {
continue;
}
m_ls_cell_ids.insert (std::make_pair (*c, index));
auto cs = cell_specs.getCellSpecs ()[index];
const db::Cell &cell = mp_layout->cell (*c);
cs.setName (mp_layout->cell_name (*c));
const db::LibraryProxy *lib_proxy = dynamic_cast<const db::LibraryProxy *> (&cell);
if (lib_proxy) {
cs.setLibraryCellName (cell.get_basic_name ());
cs.setLibraryRefId (get_library_ref_id (lib_proxy->lib_id ()));
}
if (mp_layout->is_pcell_instance (*c).first) {
// Only PCells have a "parameters" object. Others won't initialize "parameters"
auto param_dict = mp_layout->get_named_pcell_parameters (*c);
auto pcell_parameters = cs.getParameters ().initValues (param_dict.size ());
size_t pindex = 0;
for (auto p = param_dict.begin (); p != param_dict.end (); ++p, ++pindex) {
auto pn = pcell_parameters[pindex];
pn.setNameId (get_property_name_id_from_variant (tl::Variant (p->first)));
make_variant_value (p->second, pn.getValue ());
}
}
cs.setPropertySetId (get_property_id (cell.prop_id ()));
++index;
}
}
/**
* @brief Gets the LStream Id for a given KLayout cell Id
*
* Note that the "make_cell_specs" must have been executed, before this method
* can be used.
*/
uint64_t
Writer::get_cell_id (db::cell_index_type ci)
{
auto i = m_ls_cell_ids.find (ci);
tl_assert (i != m_ls_cell_ids.end ());
return i->second;
}
/**
* @brief Produces the cell hierarchy tree on stream::library::CellHierarchyTree
*
* This method will consider the selected cells and produce a cell hierarchy
* tree in top-down mode.
*/
void
Writer::make_cell_hierarchy_tree (stream::library::CellHierarchyTree::Builder cell_tree)
{
size_t top_cell_count = 0;
for (auto c = mp_layout->begin_top_down (); c != mp_layout->end_top_cells (); ++c) {
if (m_cells_to_write.find (*c) != m_cells_to_write.end ()) {
++top_cell_count;
}
}
cell_tree.setNumberOfTopCells (top_cell_count);
cell_tree.initNodes (m_cells_to_write.size ());
size_t index = 0;
for (auto c = mp_layout->begin_top_down (); c != mp_layout->end_top_down (); ++c) {
if (m_cells_to_write.find (*c) == m_cells_to_write.end ()) {
continue;
}
auto cn = cell_tree.getNodes ()[index];
cn.setCellId (get_cell_id (*c));
std::set<uint64_t> children;
const db::Cell &cell = mp_layout->cell (*c);
for (auto cc = cell.begin_child_cells (); ! cc.at_end (); ++cc) {
if (m_cells_to_write.find (*cc) != m_cells_to_write.end ()) {
children.insert (get_cell_id (*cc));
}
}
cn.initChildCellIds (children.size ());
size_t cindex = 0;
for (auto cc = children.begin (); cc != children.end (); ++cc, ++cindex) {
cn.getChildCellIds ().set (cindex, *cc);
}
++index;
}
// all cells have been written
tl_assert (index == m_cells_to_write.size ());
}
/**
* @brief Generates meta info for the given cell or layout in the stream::propertySet::PropertySet struct
*
* If cell is 0, the layout meta info will be generated. Otherwise the
* cell specific meta info will be produced. In both cases the meta info
* is written to the given PropertySet builder.
*/
void
Writer::make_meta_data (const db::Cell *cell, stream::metaData::MetaData::Builder meta_data)
{
auto mfrom = cell ? mp_layout->begin_meta (cell->cell_index ()) : mp_layout->begin_meta ();
auto mto = cell ? mp_layout->end_meta (cell->cell_index ()) : mp_layout->end_meta ();
size_t count = 0;
for (auto m = mfrom; m != mto; ++m) {
if (m->second.persisted) {
++count;
}
}
meta_data.initEntries (count);
size_t index = 0;
for (auto m = mfrom; m != mto; ++m) {
if (m->second.persisted) {
auto p = meta_data.getEntries ()[index];
auto name = mp_layout->meta_info_name (m->first);
p.setName (name);
p.setDescription (m->second.description);
make_variant_value (m->second.value, p.getValue ());
++index;
}
}
}
/**
* @brief Writes the cell message for the given cell, followed by the layout view message
*
* @param ci The cell for which to generate the message
* @param os The stream where to write the message
*
* This method generates a single-view cell message (the view is only a layout view).
*/
void
Writer::write_cell (db::cell_index_type ci, kj::BufferedOutputStream &os)
{
bool needs_layout_view = ! mp_layout->cell (ci).is_real_ghost_cell ();
bool needs_meta_data_view = mp_layout->begin_meta (ci) != mp_layout->end_meta (ci);
capnp::MallocMessageBuilder message;
stream::cell::Cell::Builder cell = message.initRoot<stream::cell::Cell> ();
cell.initViewIds ((needs_layout_view ? 1 : 0) + (needs_meta_data_view ? 1 : 0));
int view_index = 0;
if (needs_layout_view) {
tl_assert (m_layout_view_id >= 0);
cell.getViewIds ().set (view_index++, (unsigned int) m_layout_view_id);
}
if (needs_meta_data_view) {
tl_assert (m_meta_data_view_id >= 0);
cell.getViewIds ().set (view_index++, (unsigned int) m_meta_data_view_id);
}
writePackedMessage (os, message);
yield_progress ();
replicate_message (std::string (".cell_") + mp_layout->cell_name (ci), message);
if (needs_layout_view) {
write_layout_view (ci, os);
}
if (needs_meta_data_view) {
write_meta_data_view (ci, os);
}
}
/**
* @brief generates and writes a layout view message for the given cell
*
* @param ci The cell for which to generate the message
* @param os The stream where to write the message
*
* Generating the message involves compressing and producing the
* instances and shapes for various kinds on each layer.
*/
void
Writer::write_layout_view (db::cell_index_type ci, kj::BufferedOutputStream &os)
{
capnp::MallocMessageBuilder message;
auto layout_view = message.initRoot<stream::layoutView::LayoutView> ();
const db::Cell &cell = mp_layout->cell (ci);
std::vector<std::pair<unsigned int, size_t> > layers_for_cell;
layers_for_cell.reserve (m_layers_to_write.size ());
for (auto l = m_layers_to_write.begin (); l != m_layers_to_write.end (); ++l) {
if (! cell.shapes (l->first).empty ()) {
layers_for_cell.push_back (std::make_pair (l->first, l - m_layers_to_write.begin ()));
}
}
layout_view.initLayers (layers_for_cell.size ());
for (auto l = layers_for_cell.begin (); l != layers_for_cell.end (); ++l) {
auto layer = layout_view.getLayers () [l - layers_for_cell.begin ()];
layer.setLayerId (l->second);
Compressed compressed;
compressed.compress_shapes (cell.shapes (l->first), m_compression_level, m_recompress);
layer.initRepetitions (compressed.num_arrays ());
for (auto r = compressed.begin_regular_arrays (); r != compressed.end_regular_arrays (); ++r) {
tl_assert (r->second > 0);
make_repetition (r->first, layer.getRepetitions ()[r->second - 1]);
}
for (auto r = compressed.begin_irregular_arrays (); r != compressed.end_irregular_arrays (); ++r) {
tl_assert (r->second > 0);
make_repetition (r->first, layer.getRepetitions ()[r->second - 1]);
}
make_objects (compressed.get_container<db::Point> (), layer.getPoints ());
make_objects (compressed.get_container<db::Box> (), layer.getBoxes ());
make_objects (compressed.get_container<db::Edge> (), layer.getEdges ());
make_objects (compressed.get_container<db::EdgePair> (), layer.getEdgePairs ());
make_objects (compressed.get_container<db::Text> (), layer.getLabels ());
make_objects (compressed.get_container<db::Polygon> (), layer.getPolygons ());
make_objects (compressed.get_container<db::SimplePolygon> (), layer.getSimplePolygons ());
make_objects (compressed.get_container<db::Path> (), layer.getPaths ());
}
// collects and writes the bounding box from the layers we want to write
db::Box bbox;
for (auto l = m_layers_to_write.begin (); l != m_layers_to_write.end (); ++l) {
bbox += cell.bbox (l->first);
}
make_object (bbox, layout_view.getBoundingBox ());
// instances
{
Compressed compressed;
compressed.compress_instances (cell.begin (), m_cells_to_write, m_compression_level);
layout_view.initInstanceRepetitions (compressed.num_arrays ());
for (auto r = compressed.begin_regular_arrays (); r != compressed.end_regular_arrays (); ++r) {
tl_assert (r->second > 0);
make_repetition (r->first, layout_view.getInstanceRepetitions ()[r->second - 1]);
}
for (auto r = compressed.begin_irregular_arrays (); r != compressed.end_irregular_arrays (); ++r) {
tl_assert (r->second > 0);
make_repetition (r->first, layout_view.getInstanceRepetitions ()[r->second - 1]);
}
make_objects (compressed.get_container<db::CellInstArray> (), layout_view.getInstances ());
}
writePackedMessage (os, message);
yield_progress ();
replicate_message (std::string (".lv_") + mp_layout->cell_name (ci), message);
}
/**
* @brief generates and writes a meta data view message for the given cell
*
* @param ci The cell for which to generate the message
* @param os The stream where to write the message
*/
void
Writer::write_meta_data_view (db::cell_index_type ci, kj::BufferedOutputStream &os)
{
capnp::MallocMessageBuilder message;
auto meta_data_view = message.initRoot<stream::metaDataView::MetaDataView> ();
make_meta_data (&mp_layout->cell (ci), meta_data_view.getData ());
writePackedMessage (os, message);
yield_progress ();
replicate_message (std::string (".lv_") + mp_layout->cell_name (ci), message);
}
/**
* @brief Creates a regular repetition from the RegularArray object
*
* The regular array should not be a null array.
*/
void
Writer::make_repetition (const RegularArray &array, stream::repetition::Repetition::Builder builder)
{
if (array.a ().y () == 0 && array.b ().x () == 0) {
auto regular = builder.getTypes ().initRegularOrtho ();
regular.setDx (array.a ().x ());
regular.setDy (array.b ().y ());
regular.setNx (array.na ());
regular.setNy (array.nb ());
} else if (array.a ().x () == 0 && array.b ().y () == 0) {
auto regular = builder.getTypes ().initRegularOrtho ();
regular.setDx (array.b ().x ());
regular.setDy (array.a ().y ());
regular.setNx (array.nb ());
regular.setNy (array.na ());
} else {
auto regular = builder.getTypes ().initRegular ();
auto a = regular.getA ();
a.setDx (array.a ().x ());
a.setDy (array.a ().y ());
auto b = regular.getB ();
b.setDx (array.b ().x ());
b.setDy (array.b ().y ());
regular.setNa (array.na ());
regular.setNb (array.nb ());
}
}
/**
* @brief Creates an enumerated stream::repetition::Repetition object from a sequence of displacements
*
* The list of displacements is directly converted into the repetition object.
* The implied zero-displacement element at the beginning must not be added at the front.
*/
void
Writer::make_repetition (const std::vector<db::Vector> &disp_array, stream::repetition::Repetition::Builder builder)
{
auto enumerated = builder.getTypes ().initEnumerated ();
enumerated.initDeltas (disp_array.size ());
size_t index = 0;
db::Vector dl;
for (auto d = disp_array.begin (); d != disp_array.end (); ++d, ++index) {
auto delta = enumerated.getDeltas ()[index];
db::Vector dd = *d - dl;
dl = *d;
delta.setDx (dd.x ());
delta.setDy (dd.y ());
}
}
/**
* @brief Creates a stream::geometry::Contour struct from a sequence of points
*
* @param begin The begin iterator for the point sequence
* @param end The end iterator for the point sequence
* @param n The number of points
* @param builder The stream::geometry::Contour builder
*/
template <class Iter>
static void
make_contour (Iter begin, Iter end, size_t n, stream::geometry::Contour::Builder builder)
{
auto p = begin;
tl_assert (n > 0);
db::Point pl = *p;
builder.getP1 ().setX (pl.x ());
builder.getP1 ().setY (pl.y ());
++p;
builder.initDeltas (n - 1);
size_t index = 0;
for ( ; p != end; ++p, ++index) {
auto d = builder.getDeltas ()[index];
auto pd = *p - pl;
d.setDx (pd.x ());
d.setDy (pd.y ());
pl = *p;
}
}
/**
* @brief "make_object" overload for db::SimplePolygon and stream::layoutView::SimplePolygon
*/
void
Writer::make_object (const db::SimplePolygon &obj, stream::geometry::SimplePolygon::Builder cpnp_obj)
{
make_contour (obj.hull ().begin (), obj.hull ().end (), obj.hull ().size (), cpnp_obj.getHull ());
}
/**
* @brief "make_object" overload for db::Polygon and stream::layoutView::Polygon
*/
void
Writer::make_object (const db::Polygon &obj, stream::geometry::Polygon::Builder cpnp_obj)
{
make_contour (obj.hull ().begin (), obj.hull ().end (), obj.hull ().size (), cpnp_obj.getHull ());
cpnp_obj.initHoles (obj.holes ());
for (unsigned int h = 0; h < obj.holes (); ++h) {
make_contour (obj.hole (h).begin (), obj.hole (h).end (), obj.hole (h).size (), cpnp_obj.getHoles ()[h]);
}
}
/**
* @brief "make_object" overload for db::Edge and stream::layoutView::Edge
*/
void
Writer::make_object (const db::Edge &obj, stream::geometry::Edge::Builder cpnp_obj)
{
auto p1 = cpnp_obj.getP1 ();
p1.setX (obj.p1 ().x ());
p1.setY (obj.p1 ().y ());
auto delta = cpnp_obj.getDelta ();
delta.setDx (obj.d ().x ());
delta.setDy (obj.d ().y ());
}
/**
* @brief "make_object" overload for db::EdgePair and stream::layoutView::EdgePair
*/
void
Writer::make_object (const db::EdgePair &obj, stream::geometry::EdgePair::Builder cpnp_obj)
{
make_object (obj.first (), cpnp_obj.getE1 ());
make_object (obj.second (), cpnp_obj.getE2 ());
}
/**
* @brief "make_object" overload for db::Box and stream::layoutView::Box
*/
void
Writer::make_object (const db::Box &obj, stream::geometry::Box::Builder cpnp_obj)
{
auto p1 = cpnp_obj.getP1 ();
p1.setX (obj.p1 ().x ());
p1.setY (obj.p1 ().y ());
auto delta = cpnp_obj.getDelta ();
auto d = obj.p2 () - obj.p1 ();
delta.setDx (d.x ());
delta.setDy (d.y ());
}
/**
* @brief Converts KLayout's fixpoint transformation code into a stream::geometry::FixPointTransformation enum
*/
stream::geometry::FixPointTransformation
Writer::make_fixpoint_transformation (const db::Trans &trans)
{
switch (trans.fp_trans ().rot ()) {
case db::FTrans::r0:
default:
return stream::geometry::FixPointTransformation::R0;
case db::FTrans::r90:
return stream::geometry::FixPointTransformation::R90;
case db::FTrans::r180:
return stream::geometry::FixPointTransformation::R180;
case db::FTrans::r270:
return stream::geometry::FixPointTransformation::R270;
case db::FTrans::m0:
return stream::geometry::FixPointTransformation::M0;
case db::FTrans::m45:
return stream::geometry::FixPointTransformation::M45;
case db::FTrans::m90:
return stream::geometry::FixPointTransformation::M90;
case db::FTrans::m135:
return stream::geometry::FixPointTransformation::M135;
}
}
/**
* @brief "make_object" overload for db::Text and stream::layoutView::Text
*/
void
Writer::make_object (const db::Text &obj, stream::geometry::Label::Builder cpnp_obj)
{
db::Point pos = db::Point () + obj.trans ().disp ();
cpnp_obj.getPosition ().setX (pos.x ());
cpnp_obj.getPosition ().setY (pos.y ());
cpnp_obj.setOrientation (make_fixpoint_transformation (obj.trans ()));
cpnp_obj.setStringId (get_text_string_id (obj.string ()));
cpnp_obj.setSize (obj.size ());
switch (obj.halign ()) {
case db::HAlignCenter:
cpnp_obj.setHorizontalAlign (stream::geometry::Label::HAlignment::CENTER);
break;
case db::HAlignRight:
cpnp_obj.setHorizontalAlign (stream::geometry::Label::HAlignment::RIGHT);
break;
case db::HAlignLeft:
default:
cpnp_obj.setHorizontalAlign (stream::geometry::Label::HAlignment::LEFT);
}
switch (obj.valign ()) {
case db::VAlignCenter:
cpnp_obj.setVerticalAlign (stream::geometry::Label::VAlignment::CENTER);
break;
case db::VAlignTop:
cpnp_obj.setVerticalAlign (stream::geometry::Label::VAlignment::TOP);
break;
case db::VAlignBottom:
default:
cpnp_obj.setVerticalAlign (stream::geometry::Label::VAlignment::BOTTOM);
}
}
/**
* @brief "make_object" overload for db::Point and stream::layoutView::Point
*/
void
Writer::make_object (const db::Point &obj, stream::geometry::Point::Builder cpnp_obj)
{
cpnp_obj.setX (obj.x ());
cpnp_obj.setY (obj.y ());
}
/**
* @brief "make_object" overload for db::Path and stream::layoutView::Path
*/
void
Writer::make_object (const db::Path &obj, stream::geometry::Path::Builder cpnp_obj)
{
make_contour (obj.begin (), obj.end (), obj.points (), cpnp_obj.getSpine ());
if ((obj.width () / 2) * 2 != obj.width ()) {
warn (tl::to_string (tr ("Rounding width to even DBU value in path: ")) + obj.to_string ());
}
cpnp_obj.setHalfWidth (obj.width () / 2);
if (obj.round ()) {
if (obj.bgn_ext () != obj.end_ext () || obj.bgn_ext () * 2 != obj.width ()) {
warn (tl::to_string (tr ("Changing elliptic-end path to circular ends: ")) + obj.to_string ());
}
cpnp_obj.setExtensionType (stream::geometry::Path::ExtensionType::ROUND);
} else if (obj.bgn_ext () * 2 == obj.width () && obj.bgn_ext () == obj.end_ext ()) {
cpnp_obj.setExtensionType (stream::geometry::Path::ExtensionType::SQUARE);
} else if (obj.bgn_ext () == 0 && obj.end_ext () == 0) {
cpnp_obj.setExtensionType (stream::geometry::Path::ExtensionType::FLUSH);
} else {
cpnp_obj.setExtensionType (stream::geometry::Path::ExtensionType::VARIABLE);
cpnp_obj.setBeginExtension (obj.bgn_ext ());
cpnp_obj.setEndExtension (obj.end_ext ());
}
}
/**
* @brief "make_object" overload for db::CellInstArray and stream::layoutView::CellInstance
*/
void
Writer::make_object (const db::CellInstArray &obj, stream::layoutView::CellInstance::Builder cpnp_obj)
{
// NOTE: the "CellInstArray" will actually be a single instance always
tl_assert (obj.size () == 1);
cpnp_obj.setCellId (get_cell_id (obj.object ().cell_index ()));
auto transformation = cpnp_obj.getTransformation ();
db::Point pos = db::Point () + obj.front ().disp ();
transformation.getDisplacement ().setDx (pos.x ());
transformation.getDisplacement ().setDy (pos.y ());
if (! obj.is_complex ()) {
auto simple = transformation.getTransformation ().initSimple ();
simple.setOrientation (make_fixpoint_transformation (obj.front ()));
} else {
db::ICplxTrans trans = obj.complex_trans ();
auto complex = transformation.getTransformation ().initComplex ();
complex.setScale (trans.mag ());
complex.setAngle (trans.angle ());
complex.setMirror (trans.is_mirror ());
}
}
/**
* @brief Writes the given compressed container to the container builder
*
* "Object" is an object that is supported by the compression scheme
* (those are db::CellInstArray and the geometrical primitives such as db::Box etc.).
*
* "Builder" is the Builder object of a "ObjectContainerForType" generic struct.
*
* This method will use the "make_object" overloads to actually create the objects.
* The "compressed_container" will contain various variants involved plain and
* arrayed objects, optionally combined with properties.
*
* These schemes are placed in the corresponding slots of the "ObjectContainerForType"
* struct.
*
* This method is called after the compressed objects have been computed.
* This also involves generating repetition Ids which are already available when
* this method is called.
*/
template <class Object, class Builder>
void
Writer::make_objects (const Compressed::compressed_container<Object> &container, Builder builder)
{
size_t i;
builder.initBasic (container.plain.size ());
i = 0;
for (auto s = container.plain.begin (); s != container.plain.end (); ++s, ++i) {
make_object (*s, builder.getBasic ()[i].getBasic ());
}
builder.initArrays (container.array.size ());
i = 0;
for (auto s = container.array.begin (); s != container.array.end (); ++s, ++i) {
auto a = builder.getArrays ()[i];
make_object (s->first, a.getBasic ());
a.setRepetitionId (s->second);
}
builder.initWithProperties (container.with_properties.size ());
i = 0;
for (auto s = container.with_properties.begin (); s != container.with_properties.end (); ++s, ++i) {
auto a = builder.getWithProperties ()[i];
make_object (s->first, a.getBasic ());
a.setPropertySetId (get_property_id (s->second));
}
builder.initArraysWithProperties (container.array_with_properties.size ());
i = 0;
for (auto s = container.array_with_properties.begin (); s != container.array_with_properties.end (); ++s, ++i) {
auto a = builder.getArraysWithProperties ()[i];
auto ab = a.getBasic ();
make_object (s->first.first, ab.getBasic ());
ab.setRepetitionId (s->second);
a.setPropertySetId (get_property_id (s->first.second));
}
}
} // namespace db