diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index 8a331dda6..6367fa374 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -24,6 +24,8 @@ #include "dbLoadLayoutOptions.h" #include "tlCommandLineParser.h" +#include "tlStream.h" + namespace bd { @@ -153,13 +155,44 @@ GenericReaderOptions::add_options (tl::CommandLineOptions &cmd) "layer is specified, all source layers addressed with the source specification are " "combined into this target layer.\n" "\n" + "For clarity, source and target specifications can be enclosed in round or square brackets. " + "With square brackets, the default target is '*/*' which results in the expansion of a source " + "layer range.\n" + "\n" + "To clone layers, add a mapping statement beginning with a '+' character. While other mapping statements " + "redefine mappings established before, mapping statement starting with '+' will clone the layer (1:m mapping).\n" + "\n" + "You can cancel mappings established before by using an 'unmap' statement. Such a statement " + "begins with a '-' and lists the layers whose mapping is to be removed. This is useful for creating " + "'mapping holes' in sequences.\n" + "\n" + "If brackets are used, '+' (multi-mapping) and '-' (unmapping) needs to go before the brackets.\n" + "\n" "Examples:\n" "\n" "* 1/0 2/0 3/0-255:17/0\n" - " Selects 1/0, 2/0 and maps layer 3, datatype 0 to 255 to layer 17, datatype 0\n" + " Selects 1/0, 2/0 and maps layer 3, datatype 0 to 255 to layer 17, datatype 0.\n" + " If clarity, the mapping can also be written with brackets like this: '(1/0) (2/0) (3/0-255:17/0)'.\n" "\n" "* A:1/0 B:2/0\n" - " Maps named layer A to 1/0 and named layer B to 2/0" + " Maps named layer A to 1/0 and named layer B to 2/0.\n" + " If clarity, the mapping can also be written with brackets like this: '(A:1/0) (B:2/0)'.\n" + "\n" + "* [*/*] +(10/*:1000)/*\n" + " Includes all layers, but in addition copies all datatypes of layer 10 to 1000 while keeping the datatype.\n" + " Note the square bracket which implies range expansion and how the brackets give a visual aid for the " + " grouping of the mapping parts.\n" + "\n" + "* [*/*] -(10/*)\n" + " Includes all layers, but drops all datatypes from layer 10 through 'unmapping'.\n" + " Please note, that this specification requires -" + m_prefix + "s (skip unknown layers) because otherwise the " + " unmapped layers are still created through the unknown layer fallback path.\n" + ) + << tl::arg (group + + "--" + m_long_prefix + "layer-map-file=map", this, &GenericReaderOptions::set_layer_map_file, "Specifies the layer mapping for the input as a file", + "This option specifies the layer selection or mapping like -" + m_prefix + "m, but takes the mapping from the given file. " + "Each line in this file is read as one layer mapping expression. " + "Empty lines or lines starting with a hash (#) character or with double slashes (//) are ignored." ) ; } @@ -604,12 +637,19 @@ void GenericReaderOptions::set_layer_map (const std::string &lm) int l = 0; while (! ex.at_end ()) { - m_layer_map.map_expr (ex, l); + m_layer_map.add_expr (ex, l); ex.test ("//"); ++l; } } +void GenericReaderOptions::set_layer_map_file (const std::string &lm) +{ + tl::InputStream file (lm); + tl::TextInputStream text (file); + m_layer_map = db::LayerMap::from_string_file_format (text.read_all ()); +} + void GenericReaderOptions::set_read_named_layers (bool f) { m_keep_layer_names = f; diff --git a/src/buddies/src/bd/bdReaderOptions.h b/src/buddies/src/bd/bdReaderOptions.h index 7a0408ead..9caa9496c 100644 --- a/src/buddies/src/bd/bdReaderOptions.h +++ b/src/buddies/src/bd/bdReaderOptions.h @@ -134,6 +134,7 @@ private: std::vector m_magic_lib_path; void set_layer_map (const std::string &lm); + void set_layer_map_file (const std::string &lm); void set_dbu (double dbu); void set_read_named_layers (bool f); diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 763633f3e..e38ea7c5d 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -19,6 +19,7 @@ SOURCES = \ dbClipboard.cc \ dbClipboardData.cc \ dbClip.cc \ + dbColdProxy.cc \ dbCommonReader.cc \ dbCompoundOperation.cc \ dbEdge.cc \ @@ -219,6 +220,7 @@ HEADERS = \ dbClipboardData.h \ dbClipboard.h \ dbClip.h \ + dbColdProxy.h \ dbCommonReader.h \ dbCompoundOperation.h \ dbEdge.h \ diff --git a/src/db/db/dbColdProxy.cc b/src/db/db/dbColdProxy.cc new file mode 100644 index 000000000..33346ef91 --- /dev/null +++ b/src/db/db/dbColdProxy.cc @@ -0,0 +1,128 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "dbColdProxy.h" +#include "dbLibraryManager.h" +#include "dbLibrary.h" +#include "dbLayout.h" +#include "dbLayoutUtils.h" + +#include "tlThreads.h" + +namespace db +{ + +static tl::Mutex s_map_mutex; +static std::map > s_proxies_per_library_name; + +const tl::weak_collection & +ColdProxy::cold_proxies_per_lib_name (const std::string &libname) +{ + tl::MutexLocker locker (&s_map_mutex); + + std::map >::const_iterator i = s_proxies_per_library_name.find (libname); + if (i != s_proxies_per_library_name.end ()) { + return i->second; + } else { + static tl::weak_collection s_empty; + return s_empty; + } +} + +ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info) + : Cell (ci, layout), mp_context_info (new ProxyContextInfo (info)) +{ + if (! info.lib_name.empty ()) { + tl::MutexLocker locker (&s_map_mutex); + s_proxies_per_library_name [info.lib_name].push_back (this); + } +} + +ColdProxy::~ColdProxy () +{ + delete mp_context_info; + mp_context_info = 0; +} + +Cell * +ColdProxy::clone (Layout &layout) const +{ + Cell *cell = new ColdProxy (db::Cell::cell_index (), layout, *mp_context_info); + // copy the cell content + *cell = *this; + return cell; +} + +std::string +ColdProxy::get_basic_name () const +{ + if (! mp_context_info->pcell_name.empty ()) { + return "" + mp_context_info->pcell_name; + } else if (! mp_context_info->cell_name.empty ()) { + return "" + mp_context_info->cell_name; + } else { + return Cell::get_basic_name (); + } +} + +std::string +ColdProxy::get_display_name () const +{ + if (! mp_context_info->lib_name.empty ()) { + std::string stem = "" + mp_context_info->lib_name + "."; + if (! mp_context_info->pcell_name.empty ()) { + return stem + mp_context_info->pcell_name; + } else if (! mp_context_info->cell_name.empty ()) { + return stem + mp_context_info->cell_name; + } else { + return stem + ""; + } + } else { + return Cell::get_display_name (); + } +} + +std::string +ColdProxy::get_qualified_name () const +{ + if (! mp_context_info->lib_name.empty ()) { + std::string stem = "" + mp_context_info->lib_name + "."; + if (! mp_context_info->pcell_name.empty ()) { + if (mp_context_info->pcell_parameters.empty ()) { + return stem + mp_context_info->pcell_name; + } else { + // TODO: list parameters? Might be long. + return stem + mp_context_info->pcell_name + "(...)"; + } + } else if (! mp_context_info->cell_name.empty ()) { + return stem + mp_context_info->cell_name; + } else { + return stem + ""; + } + } else { + return Cell::get_qualified_name (); + } +} + +} + diff --git a/src/db/db/dbColdProxy.h b/src/db/db/dbColdProxy.h new file mode 100644 index 000000000..13a586c1c --- /dev/null +++ b/src/db/db/dbColdProxy.h @@ -0,0 +1,111 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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_dbColdProxy +#define HDR_dbColdProxy + +#include "dbCommon.h" + +#include "dbTypes.h" +#include "dbCell.h" + +#include "tlObject.h" + +namespace db +{ + +struct ProxyContextInfo; + +/** + * @brief A cell specialization: a cold proxy representing a library or PCell which has gone out of scope + * + * If a PCell or library cell gets disconnected - for example, because the technology has changed or during + * development of PCell code - this proxy replaces the original one. It stores the connection information, so + * it can be regenerated when it becomes valid again. + */ +class DB_PUBLIC ColdProxy + : public Cell, public tl::Object +{ +public: + /** + * @brief The constructor + * + * Creates a cold proxy represented by the ProxyContextInfo data. + */ + ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info); + + /** + * @brief The destructor + */ + ~ColdProxy (); + + /** + * @brief Cloning + */ + virtual Cell *clone (Layout &layout) const; + + /** + * @brief Get the library id + */ + const ProxyContextInfo &context_info () const + { + return *mp_context_info; + } + + /** + * @brief Indicates that this cell is a proxy cell + */ + virtual bool is_proxy () const + { + return true; + } + + /** + * @brief Gets a list of cold proxies for a given library name + */ + static const tl::weak_collection &cold_proxies_per_lib_name (const std::string &libname); + + /** + * @brief Gets the basic name + */ + virtual std::string get_basic_name () const; + + /** + * @brief Gets the display name + */ + virtual std::string get_display_name () const; + + /** + * @brief Gets the qualified name + */ + virtual std::string get_qualified_name () const; + +private: + ProxyContextInfo *mp_context_info; +}; + +} + +#endif + + diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index 2d4afc86d..f52e5cdf2 100644 --- a/src/db/db/dbCommonReader.cc +++ b/src/db/db/dbCommonReader.cc @@ -35,7 +35,7 @@ namespace db static const size_t null_id = std::numeric_limits::max (); CommonReader::CommonReader () - : m_cc_resolution (AddToCell) + : m_cc_resolution (AddToCell), m_create_layers (false) { // .. nothing yet .. } @@ -275,6 +275,46 @@ CommonReader::merge_cell_without_instances (db::Layout &layout, db::cell_index_t layout.delete_cell (src_cell.cell_index ()); } +const db::LayerMap & +CommonReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) +{ + init (options); + + m_common_options.layer_map.prepare (layout); + + layout.start_changes (); + try { + do_read (layout); + finish (layout); + layout.end_changes (); + } catch (...) { + layout.end_changes (); + throw; + } + + return m_layer_map_out; +} + +const db::LayerMap & +CommonReader::read (db::Layout &layout) +{ + return read (layout, db::LoadLayoutOptions ()); +} + +void +CommonReader::init (const LoadLayoutOptions &options) +{ + m_common_options = options.get_options (); + m_cc_resolution = m_common_options.cell_conflict_resolution; + m_create_layers = m_common_options.create_other_layers; + + m_layer_map_out.clear (); + m_multi_mapping_placeholders.clear (); + m_layer_cache.clear (); + m_layers_created.clear (); + m_layer_names.clear (); +} + void CommonReader::finish (db::Layout &layout) { @@ -382,6 +422,120 @@ CommonReader::finish (db::Layout &layout) } } + + // resolve layer multi-mapping + + for (std::map, unsigned int>::const_iterator i = m_multi_mapping_placeholders.begin (); i != m_multi_mapping_placeholders.end (); ++i) { + + if (i->first.size () > 1) { + + bool discard_layer = i->first.find (i->second) == i->first.end (); + + for (std::set::const_iterator l = i->first.begin (); l != i->first.end (); ++l) { + + // last one? this one will get a "move" + std::set::const_iterator ll = l; + if (discard_layer && ++ll == i->first.end ()) { + layout.move_layer (i->second, *l); + layout.delete_layer (i->second); + } else { + layout.copy_layer (i->second, *l); + } + + } + + } + + } + + // rename layers created before if required + + for (std::set::const_iterator i = m_layers_created.begin (); i != m_layers_created.end (); ++i) { + + const db::LayerProperties &lp = layout.get_properties (*i); + + const tl::interval_map *dtmap = layer_names ().mapped (lp.layer); + const std::string *name = 0; + if (dtmap) { + name = dtmap->mapped (lp.datatype); + } + + if (name) { + // need to rename: add a new madding to m_layer_map_out and adjust the layout's layer properties + db::LayerProperties lpp = lp; + join_layer_names (lpp.name, *name); + layout.set_properties (*i, lpp); + m_layer_map_out.map (LDPair (lp.layer, lp.datatype), *i, lpp); + } + + } +} + +std::pair +CommonReader::open_dl (db::Layout &layout, const LDPair &dl) +{ + std::map >::const_iterator lc = m_layer_cache.find (dl); + if (lc != m_layer_cache.end ()) { + return lc->second; + } else { + std::pair res = open_dl_uncached (layout, dl); + m_layer_cache.insert (std::make_pair (dl, res)); + return res; + } +} + +std::pair +CommonReader::open_dl_uncached (db::Layout &layout, const LDPair &dl) +{ + const std::set &li = common_options ().layer_map.logical (dl, layout); + if (li.empty ()) { + + if (! m_create_layers) { + return std::make_pair (false, (unsigned int) 0); + } + + // and create the layer + db::LayerProperties lp; + lp.layer = dl.layer; + lp.datatype = dl.datatype; + + // resolve OASIS name if possible + const tl::interval_map *names_dmap = m_layer_names.mapped (dl.layer); + if (names_dmap != 0) { + const std::string *name = names_dmap->mapped (dl.datatype); + if (name != 0) { + lp.name = *name; + } + } + + unsigned int nl = layout.insert_layer (lp); + m_layer_map_out.map (dl, nl, lp); + + m_layers_created.insert (nl); + + return std::make_pair (true, nl); + + } else if (li.size () == 1) { + + m_layer_map_out.map (dl, *li.begin (), layout.get_properties (*li.begin ())); + + return std::make_pair (true, *li.begin ()); + + } else { + + for (std::set::const_iterator i = li.begin (); i != li.end (); ++i) { + m_layer_map_out.mmap (dl, *i, layout.get_properties (*i)); + } + + std::map, unsigned int>::iterator mmp = m_multi_mapping_placeholders.find (li); + if (mmp == m_multi_mapping_placeholders.end ()) { + // create a placeholder layer + mmp = m_multi_mapping_placeholders.insert (std::make_pair (li, layout.insert_layer ())).first; + } + + return std::make_pair (true, mmp->second); + + } } // --------------------------------------------------------------- diff --git a/src/db/db/dbCommonReader.h b/src/db/db/dbCommonReader.h index f0ed56bcf..81d361672 100644 --- a/src/db/db/dbCommonReader.h +++ b/src/db/db/dbCommonReader.h @@ -31,6 +31,104 @@ namespace db { +/** + * @brief The CellConflictResolution enum + */ +enum CellConflictResolution +{ + AddToCell = 0, + OverwriteCell = 1, + SkipNewCell = 2, + RenameCell = 3 +}; + +/** + * @brief Structure that holds the GDS2 and OASIS specific options for the reader + */ +class DB_PUBLIC CommonReaderOptions + : public FormatSpecificReaderOptions +{ +public: + /** + * @brief The constructor + */ + CommonReaderOptions () + : create_other_layers (true), + enable_text_objects (true), + enable_properties (true), + cell_conflict_resolution (CellConflictResolution::AddToCell) + { + // .. nothing yet .. + } + + /** + * @brief Specifies a layer mapping + * + * If a layer mapping is specified, only the given layers are read. + * Otherwise, all layers are read. + * Setting "create_other_layers" to true will make the reader + * create other layers for all layers not given in the layer map. + * Setting an empty layer map and create_other_layers to true effectively + * enables all layers for reading. + */ + db::LayerMap layer_map; + + /** + * @brief A flag indicating that a new layers shall be created + * + * If this flag is set to true, layers not listed in the layer map a created + * too. + */ + bool create_other_layers; + + /** + * @brief A flag indicating whether to read text objects + * + * If this flag is set to true, text objects are read. Otherwise they are ignored. + */ + bool enable_text_objects; + + /** + * @brief A flag indicating whether to read user properties + * + * If this flag is set to true, user properties are read. Otherwise they are ignored. + */ + bool enable_properties; + + /** + * @brief Specifies the cell merge behavior + * + * This enum controls how cells are read if a cell with the requested name already + * exists. + * + * AddToCell In this mode, instances or shapes are added to any existing cell + * OverwriteCell Overwrite existing cell. If the existing cell has children, those are removed unless used otherwise + * SkipNewCell Ignore the new cell and it's children + * RenameCell Rename the new cell + * + * If the existing opr the new cell is a ghost cell, AddToCell is applied always. In other words, + * ghost cells are always merged. + */ + CellConflictResolution cell_conflict_resolution; + + /** + * @brief Implementation of FormatSpecificReaderOptions + */ + virtual FormatSpecificReaderOptions *clone () const + { + return new CommonReaderOptions (*this); + } + + /** + * @brief Implementation of FormatSpecificReaderOptions + */ + virtual const std::string &format_name () const + { + static const std::string n ("Common"); + return n; + } +}; + /** * @brief A common reader base for GDS2 and OASIS providing common services for both readers */ @@ -38,38 +136,13 @@ class DB_PUBLIC CommonReader : public ReaderBase { public: - /** - * @brief The CellConflictResolution enum - */ - enum CellConflictResolution - { - AddToCell = 0, - OverwriteCell = 1, - SkipNewCell = 2, - RenameCell = 3 - }; + typedef tl::interval_map > layer_name_map; /** * @brief Constructor */ CommonReader (); - /** - * @brief Sets the cell name conflict resolution mode - */ - void set_cell_conflict_resolution (CellConflictResolution cc_resolution) - { - m_cc_resolution = cc_resolution; - } - - /** - * @brief Sets the cell name conflict resolution mode - */ - CellConflictResolution cell_conflict_resolution () const - { - return m_cc_resolution; - } - /** * @brief Make a cell from a name */ @@ -132,9 +205,17 @@ public: */ void finish (db::Layout &layout); + // Reimplementation of the ReaderBase interace + virtual const db::LayerMap &read (db::Layout &layout, const db::LoadLayoutOptions &options); + virtual const db::LayerMap &read (db::Layout &layout); + protected: + friend class CommonReaderLayerMapping; + virtual void common_reader_error (const std::string &msg) = 0; virtual void common_reader_warn (const std::string &msg) = 0; + virtual void do_read (db::Layout &layout) = 0; + virtual void init (const LoadLayoutOptions &options); /** * @brief Merge (and delete) the src_cell into target_cell @@ -146,98 +227,69 @@ protected: */ void merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const; + /** + * @brief Gets the common options + */ + db::CommonReaderOptions &common_options () + { + return m_common_options; + } + + /** + * @brief Gets the layer name map + */ + layer_name_map &layer_names () + { + return m_layer_names; + } + + /** + * @brief Enters the a layer with a given layer/datatype + */ + std::pair open_dl (db::Layout &layout, const LDPair &dl); + private: std::map > m_id_map; std::map > m_name_map; std::map m_name_for_id; CellConflictResolution m_cc_resolution; + bool m_create_layers; + db::CommonReaderOptions m_common_options; + db::LayerMap m_layer_map_out; + tl::interval_map > m_layer_names; + std::map > m_layer_cache; + std::map, unsigned int> m_multi_mapping_placeholders; + std::set m_layers_created; + + std::pair open_dl_uncached (db::Layout &layout, const LDPair &dl); }; /** - * @brief Structure that holds the GDS2 and OASIS specific options for the reader + * @brief A utility class that maps the layers for the proxy cell recovery */ -class DB_PUBLIC CommonReaderOptions - : public FormatSpecificReaderOptions +class CommonReaderLayerMapping + : public db::ImportLayerMapping { public: - /** - * @brief The constructor - */ - CommonReaderOptions () - : create_other_layers (true), - enable_text_objects (true), - enable_properties (true), - cell_conflict_resolution (CommonReader::AddToCell) + CommonReaderLayerMapping (db::CommonReader *reader, db::Layout *layout) + : mp_reader (reader), mp_layout (layout) { // .. nothing yet .. } - /** - * @brief Specifies a layer mapping - * - * If a layer mapping is specified, only the given layers are read. - * Otherwise, all layers are read. - * Setting "create_other_layers" to true will make the reader - * create other layers for all layers not given in the layer map. - * Setting an empty layer map and create_other_layers to true effectively - * enables all layers for reading. - */ - db::LayerMap layer_map; - - /** - * @brief A flag indicating that a new layers shall be created - * - * If this flag is set to true, layers not listed in the layer map a created - * too. - */ - bool create_other_layers; - - /** - * @brief A flag indicating whether to read text objects - * - * If this flag is set to true, text objects are read. Otherwise they are ignored. - */ - bool enable_text_objects; - - /** - * @brief A flag indicating whether to read user properties - * - * If this flag is set to true, user properties are read. Otherwise they are ignored. - */ - bool enable_properties; - - /** - * @brief Specifies the cell merge behavior - * - * This enum controls how cells are read if a cell with the requested name already - * exists. - * - * AddToCell In this mode, instances or shapes are added to any existing cell - * OverwriteCell Overwrite existing cell. If the existing cell has children, those are removed unless used otherwise - * SkipNewCell Ignore the new cell and it's children - * RenameCell Rename the new cell - * - * If the existing opr the new cell is a ghost cell, AddToCell is applied always. In other words, - * ghost cells are always merged. - */ - CommonReader::CellConflictResolution cell_conflict_resolution; - - /** - * @brief Implementation of FormatSpecificReaderOptions - */ - virtual FormatSpecificReaderOptions *clone () const + std::pair map_layer (const db::LayerProperties &lprops) { - return new CommonReaderOptions (*this); + // named layers that are imported from a library are ignored + if (lprops.is_named ()) { + return std::make_pair (false, 0); + } else { + return mp_reader->open_dl (*mp_layout, LDPair (lprops.layer, lprops.datatype)); + } } - /** - * @brief Implementation of FormatSpecificReaderOptions - */ - virtual const std::string &format_name () const - { - static const std::string n ("Common"); - return n; - } +private: + db::CommonReader *mp_reader; + db::Layout *mp_layout; }; } diff --git a/src/db/db/dbGlyphs.cc b/src/db/db/dbGlyphs.cc index ecea36e12..9cefc8b5e 100644 --- a/src/db/db/dbGlyphs.cc +++ b/src/db/db/dbGlyphs.cc @@ -174,9 +174,9 @@ TextGenerator::load_from_data (const char *data, size_t ndata, const std::string m_description = description; m_name = name; - std::pair l1 = map.logical (db::LDPair (1, 0)); - std::pair l2 = map.logical (db::LDPair (2, 0)); - std::pair l3 = map.logical (db::LDPair (3, 0)); + std::pair l1 = map.first_logical (db::LDPair (1, 0)); + std::pair l2 = map.first_logical (db::LDPair (2, 0)); + std::pair l3 = map.first_logical (db::LDPair (3, 0)); if (l1.first && l2.first) { read_from_layout (layout, l1.second, l2.second, l3.second); @@ -193,9 +193,9 @@ TextGenerator::load_from_file (const std::string &filename) m_description = filename; - std::pair l1 = map.logical (db::LDPair (1, 0)); - std::pair l2 = map.logical (db::LDPair (2, 0)); - std::pair l3 = map.logical (db::LDPair (3, 0)); + std::pair l1 = map.first_logical (db::LDPair (1, 0)); + std::pair l2 = map.first_logical (db::LDPair (2, 0)); + std::pair l3 = map.first_logical (db::LDPair (3, 0)); if (l1.first && l2.first) { read_from_layout (layout, l1.second, l2.second, l3.second); diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 2e767684e..ad60aad38 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -24,11 +24,13 @@ #include "dbLayout.h" #include "dbMemStatistics.h" #include "dbTrans.h" +#include "dbTechnology.h" #include "dbShapeRepository.h" #include "dbPCellHeader.h" #include "dbPCellVariant.h" #include "dbPCellDeclaration.h" #include "dbLibraryProxy.h" +#include "dbColdProxy.h" #include "dbLibraryManager.h" #include "dbLibrary.h" #include "dbRegion.h" @@ -84,6 +86,27 @@ private: db::properties_id_type m_from, m_to; }; +struct SetLayoutTechName + : public LayoutOp +{ + SetLayoutTechName (const std::string &from, const std::string &to) + : m_from (from), m_to (to) + { } + + virtual void redo (db::Layout *layout) const + { + layout->set_technology_name_without_update (m_to); + } + + virtual void undo (db::Layout *layout) const + { + layout->set_technology_name_without_update (m_from); + } + +private: + std::string m_from, m_to; +}; + struct SetLayoutDBU : public LayoutOp { @@ -259,6 +282,65 @@ LayerIterator::operator*() const return std::pair (m_layer_index, &m_layout.get_properties (m_layer_index)); } +// ----------------------------------------------------------------- +// Implementation of the ProxyContextInfo class + +ProxyContextInfo +ProxyContextInfo::deserialize (std::vector::const_iterator from, std::vector::const_iterator to) +{ + ProxyContextInfo info; + + for (std::vector::const_iterator i = from; i != to; ++i) { + + tl::Extractor ex (i->c_str ()); + + if (ex.test ("LIB=")) { + + info.lib_name = ex.skip (); + + } else if (ex.test ("P(")) { + + std::pair vv; + + ex.read_word_or_quoted (vv.first); + ex.test (")"); + ex.test ("="); + ex.read (vv.second); + + info.pcell_parameters.insert (vv); + + } else if (ex.test ("PCELL=")) { + + info.pcell_name = ex.skip (); + + } else if (ex.test ("CELL=")) { + + info.cell_name = ex.skip (); + + } + + } + + return info; +} + +void +ProxyContextInfo::serialize (std::vector &strings) +{ + if (! lib_name.empty ()) { + strings.push_back ("LIB=" + lib_name); + } + for (std::map ::const_iterator 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 ()) { + strings.push_back ("PCELL=" + pcell_name); + } + if (! cell_name.empty ()) { + strings.push_back ("CELL=" + cell_name); + } +} + // ----------------------------------------------------------------- // Implementation of the Layout class @@ -442,6 +524,187 @@ Layout::operator= (const Layout &d) return *this; } +const db::Technology * +Layout::technology () const +{ + return db::Technologies::instance ()->technology_by_name (m_tech_name); +} + +void +Layout::set_technology_name_without_update (const std::string &tech) +{ + if (tech != m_tech_name) { + if (manager () && manager ()->transacting ()) { + manager ()->queue (this, new SetLayoutTechName (m_tech_name, tech)); + } + m_tech_name = tech; + technology_changed_event (); + } +} + +void +Layout::set_technology_name (const std::string &tech) +{ + if (tech == m_tech_name) { + return; + } + + // determine which library to map to what + std::map mapping; + std::set seen; + std::set lost; + + for (db::Layout::iterator c = begin (); c != end (); ++c) { + + db::LibraryProxy *lib_proxy = dynamic_cast (&*c); + if (lib_proxy && seen.find (lib_proxy->lib_id ()) == seen.end ()) { + + seen.insert (lib_proxy->lib_id ()); + + std::pair new_id (false, 0); + const db::Library *l = db::LibraryManager::instance ().lib (lib_proxy->lib_id ()); + if (l) { + new_id = db::LibraryManager::instance ().lib_by_name (l->get_name (), tech); + } + + if (new_id.first && new_id.second != l->get_id ()) { + mapping.insert (std::make_pair (l->get_id (), new_id.second)); + } else if (! new_id.first) { + lost.insert (lib_proxy->lib_id ()); + } + + } + + } + + if (! mapping.empty () || ! lost.empty ()) { + + bool needs_cleanup = false; + + std::vector > pcells_to_map; + std::vector lib_cells_to_map; + std::vector lib_cells_lost; + + for (db::Layout::iterator c = begin (); c != end (); ++c) { + + std::map::const_iterator m; + + db::LibraryProxy *lib_proxy = dynamic_cast (&*c); + if (! lib_proxy) { + continue; + } + + if ((m = mapping.find (lib_proxy->lib_id ())) != mapping.end ()) { + + db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ()); + db::Cell *lib_cell = &lib->layout ().cell (lib_proxy->library_cell_index ()); + db::PCellVariant *lib_pcell = dynamic_cast (lib_cell); + if (lib_pcell) { + pcells_to_map.push_back (std::make_pair (lib_proxy, lib_pcell)); + } else { + lib_cells_to_map.push_back (lib_proxy); + } + + needs_cleanup = true; + + } else if (lost.find (lib_proxy->lib_id ()) != lost.end ()) { + + lib_cells_lost.push_back (lib_proxy); + + needs_cleanup = true; + + } + + } + + // We do PCell resolution before the library proxy resolution. The reason is that + // PCells may generate library proxies in their instantiation. Hence we must instantiate + // the PCells before we can resolve them. + for (std::vector >::const_iterator lp = pcells_to_map.begin (); lp != pcells_to_map.end (); ++lp) { + + db::cell_index_type ci = lp->first->Cell::cell_index (); + db::PCellVariant *lib_pcell = lp->second; + + std::pair pn = lib_pcell->layout ()->pcell_by_name (lp->first->get_basic_name ().c_str ()); + + if (! pn.first) { + + // substitute by a cold proxy + db::ProxyContextInfo info; + get_context_info (ci, info); + create_cold_proxy_as (info, ci); + + } else { + + db::Library *new_lib = db::LibraryManager::instance ().lib (mapping [lp->first->lib_id ()]); + + const db::PCellDeclaration *old_pcell_decl = lib_pcell->layout ()->pcell_declaration (lib_pcell->pcell_id ()); + const db::PCellDeclaration *new_pcell_decl = new_lib->layout ().pcell_declaration (pn.second); + if (! old_pcell_decl || ! new_pcell_decl) { + + // substitute by a cold proxy + db::ProxyContextInfo info; + get_context_info (ci, info); + create_cold_proxy_as (info, ci); + + } else { + + // map pcell parameters by name + std::map param_by_name = lib_pcell->parameters_by_name (); + lp->first->remap (new_lib->get_id (), new_lib->layout ().get_pcell_variant (pn.second, new_pcell_decl->map_parameters (param_by_name))); + + } + + } + + } + + for (std::vector::const_iterator lp = lib_cells_to_map.begin (); lp != lib_cells_to_map.end (); ++lp) { + + db::Library *new_lib = db::LibraryManager::instance ().lib (mapping [(*lp)->lib_id ()]); + + db::cell_index_type ci = (*lp)->Cell::cell_index (); + + std::pair cn = new_lib->layout ().cell_by_name ((*lp)->get_basic_name ().c_str ()); + + if (! cn.first) { + + // unlink this proxy: substitute by a cold proxy + db::ProxyContextInfo info; + get_context_info (ci, info); + create_cold_proxy_as (info, ci); + + } else { + + (*lp)->remap (new_lib->get_id (), cn.second); + + } + + } + + for (std::vector::const_iterator lp = lib_cells_lost.begin (); lp != lib_cells_lost.end (); ++lp) { + + db::cell_index_type ci = (*lp)->Cell::cell_index (); + + // substitute by a cold proxy + db::ProxyContextInfo info; + get_context_info (ci, info); + create_cold_proxy_as (info, ci); + + } + + if (needs_cleanup) { + cleanup (); + } + + } + + set_technology_name_without_update (tech); + + // we may have re-established a connection for pending ("cold") proxies so we can try to restore them + restore_proxies (); +} + void Layout::mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const { @@ -1800,8 +2063,37 @@ static const std::vector &gauge_parameters (const std::vectorunregister (); + if (retain_layout) { + new_cell->Cell::operator= (*old_cell); + } + } + + if (manager () && manager ()->transacting ()) { + // note the "take" method - this takes out the cell but does not delete it (we need it inside undo) + m_cells.take (iterator (old_cell)); + manager ()->queue (this, new NewRemoveCellOp (target_cell_index, cell_name (target_cell_index), true /*remove*/, old_cell)); + } else { + m_cells.erase (iterator (old_cell)); + } + + m_cells.push_back_ptr (new_cell); + m_cell_ptrs [target_cell_index] = new_cell; + + if (manager () && manager ()->transacting ()) { + manager ()->queue (this, new NewRemoveCellOp (target_cell_index, m_cell_names [target_cell_index], false /*new*/, 0)); + } +} + void -Layout::get_pcell_variant_as (pcell_id_type pcell_id, const std::vector &p, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping) +Layout::get_pcell_variant_as (pcell_id_type pcell_id, const std::vector &p, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping, bool retain_layout) { pcell_header_type *header = pcell_header (pcell_id); tl_assert (header != 0); @@ -1812,19 +2104,15 @@ Layout::get_pcell_variant_as (pcell_id_type pcell_id, const std::vectorget_variant (*this, parameters) == 0); - tl_assert (! (manager () && manager ()->transacting ())); tl_assert (m_cell_ptrs [target_cell_index] != 0); - invalidate_hier (); - - m_cells.erase (iterator (m_cell_ptrs [target_cell_index])); - pcell_variant_type *variant = new pcell_variant_type (target_cell_index, *this, pcell_id, parameters); - m_cells.push_back_ptr (variant); - m_cell_ptrs [target_cell_index] = variant; + replace_cell (target_cell_index, variant, retain_layout); - // produce the layout - variant->update (layer_mapping); + if (! retain_layout) { + // produce the layout unless we retained it + variant->update (layer_mapping); + } } cell_index_type @@ -2168,10 +2456,29 @@ Layout::get_pcell_variant_cell (cell_index_type cell_index, const std::vector &context_info) const +bool +Layout::get_context_info (cell_index_type cell_index, std::vector &strings) const +{ + ProxyContextInfo info; + if (! get_context_info (cell_index, info)) { + return false; + } else { + info.serialize (strings); + return true; + } +} + +bool +Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) const { const db::Cell *cptr = &cell (cell_index); + + const db::ColdProxy *cold_proxy = dynamic_cast (cptr); + if (cold_proxy) { + info = cold_proxy->context_info (); + return true; + } + const db::Layout *ly = this; const db::LibraryProxy *lib_proxy; @@ -2185,7 +2492,7 @@ Layout::get_context_info (cell_index_type cell_index, std::vector // one level of library indirection ly = &lib->layout (); cptr = &ly->cell (lib_proxy->library_cell_index ()); - context_info.push_back ("LIB=" + lib->get_name ()); + info.lib_name = lib->get_name (); } @@ -2199,19 +2506,36 @@ Layout::get_context_info (cell_index_type cell_index, std::vector const std::vector &pcp = pcell_decl->parameter_declarations (); std::vector::const_iterator pd = pcp.begin (); for (std::vector::const_iterator p = pcell_variant->parameters ().begin (); p != pcell_variant->parameters ().end () && pd != pcp.end (); ++p, ++pd) { - context_info.push_back ("P(" + tl::to_word_or_quoted_string (pd->get_name ()) + ")=" + p->to_parsable_string ()); + info.pcell_parameters.insert (std::make_pair (pd->get_name (), *p)); } const db::PCellHeader *header = ly->pcell_header (pcell_variant->pcell_id ()); - context_info.push_back ("PCELL=" + header->get_name ()); + info.pcell_name = header->get_name (); } else { - context_info.push_back ("CELL=" + std::string (ly->cell_name (cptr->cell_index ()))); + info.cell_name = ly->cell_name (cptr->cell_index ()); } return true; } +void +Layout::restore_proxies (ImportLayerMapping *layer_mapping) +{ + std::vector cold_proxies; + + for (iterator c = begin (); c != end (); ++c) { + db::ColdProxy *proxy = dynamic_cast (c.operator-> ()); + if (proxy) { + cold_proxies.push_back (proxy); + } + } + + for (std::vector::const_iterator p = cold_proxies.begin (); p != cold_proxies.end (); ++p) { + recover_proxy_as ((*p)->cell_index (), (*p)->context_info (), layer_mapping); + } +} + bool Layout::recover_proxy_as (cell_index_type cell_index, std::vector ::const_iterator from, std::vector ::const_iterator to, ImportLayerMapping *layer_mapping) { @@ -2219,17 +2543,21 @@ Layout::recover_proxy_as (cell_index_type cell_index, std::vector : return false; } - tl::Extractor ex (from->c_str ()); + return recover_proxy_as (cell_index, ProxyContextInfo::deserialize (from, to), layer_mapping); +} - if (ex.test ("LIB=")) { +bool +Layout::recover_proxy_as (cell_index_type cell_index, const ProxyContextInfo &info, ImportLayerMapping *layer_mapping) +{ + if (! info.lib_name.empty ()) { - std::string lib_name = ex.skip (); - Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (lib_name); - if (! lib) { - return false; + db::Cell *lib_cell = 0; + + Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (info.lib_name, m_tech_name); + if (lib) { + lib_cell = lib->layout ().recover_proxy_no_lib (info); } - db::Cell *lib_cell = lib->layout ().recover_proxy (from + 1, to); if (lib_cell) { get_lib_proxy_as (lib, lib_cell->cell_index (), cell_index, layer_mapping); return true; @@ -2237,38 +2565,28 @@ Layout::recover_proxy_as (cell_index_type cell_index, std::vector : } else { - std::map parameters; + if (! info.pcell_name.empty ()) { - while (from != to && (ex = tl::Extractor (from->c_str ())).test ("P(")) { - - std::string name; - ex.read_word_or_quoted (name); - ex.test (")"); - ex.test ("="); - - ex.read (parameters.insert (std::make_pair (name, tl::Variant ())).first->second); - - ++from; - - } - - if (ex.test ("PCELL=")) { - - std::pair pc = pcell_by_name (ex.skip ()); + std::pair pc = pcell_by_name (info.pcell_name.c_str ()); if (pc.first) { - get_pcell_variant_as (pc.second, pcell_declaration (pc.second)->map_parameters (parameters), cell_index, layer_mapping); + get_pcell_variant_as (pc.second, pcell_declaration (pc.second)->map_parameters (info.pcell_parameters), cell_index, layer_mapping); return true; } - } else if (ex.test ("CELL=")) { + } else if (! info.cell_name.empty ()) { - // This should not happen. A cell (given by the cell index) cannot be proxy to another cell in the same layout. + // This should not happen. A cell (given by the cell name) cannot be proxy to another cell in the same layout. tl_assert (false); } } + if (! dynamic_cast (m_cell_ptrs [cell_index])) { + // create a cold proxy representing the context information so we can restore it + create_cold_proxy_as (info, cell_index); + } + return false; } @@ -2279,55 +2597,54 @@ Layout::recover_proxy (std::vector ::const_iterator from, std::vect return 0; } - tl::Extractor ex (from->c_str ()); + return recover_proxy (ProxyContextInfo::deserialize (from, to)); +} - if (ex.test ("LIB=")) { +db::Cell * +Layout::recover_proxy (const ProxyContextInfo &info) +{ + if (! info.lib_name.empty ()) { - std::string lib_name = ex.skip (); - Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (lib_name); - if (! lib) { - return 0; + Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (info.lib_name, m_tech_name); + + db::Cell *lib_cell = 0; + if (lib) { + lib_cell = lib->layout ().recover_proxy_no_lib (info); } - db::Cell *lib_cell = lib->layout ().recover_proxy (from + 1, to); if (lib_cell) { - cell_index_type cell_index = get_lib_proxy (lib, lib_cell->cell_index ()); - return &cell (cell_index); + return m_cell_ptrs [get_lib_proxy (lib, lib_cell->cell_index ())]; } } else { - std::map parameters; - - while (from != to && (ex = tl::Extractor (from->c_str ())).test ("P(")) { - - std::string name; - ex.read_word_or_quoted (name); - ex.test (")"); - ex.test ("="); - - ex.read (parameters.insert (std::make_pair (name, tl::Variant ())).first->second); - - ++from; - + db::Cell *proxy = recover_proxy_no_lib (info); + if (proxy) { + return proxy; } - if (ex.test ("PCELL=")) { + } - std::pair pc = pcell_by_name (ex.skip ()); - if (pc.first) { - cell_index_type cell_index = get_pcell_variant (pc.second, pcell_declaration (pc.second)->map_parameters (parameters)); - return &cell (cell_index); - } + return m_cell_ptrs [create_cold_proxy (info)]; +} - } else if (ex.test ("CELL=")) { +db::Cell * +Layout::recover_proxy_no_lib (const ProxyContextInfo &info) +{ + if (! info.pcell_name.empty ()) { - std::pair cc = cell_by_name (ex.skip ()); - if (cc.first) { - return &cell (cc.second); - } + std::pair pc = pcell_by_name (info.pcell_name.c_str ()); + if (pc.first) { + cell_index_type cell_index = get_pcell_variant (pc.second, pcell_declaration (pc.second)->map_parameters (info.pcell_parameters)); + return m_cell_ptrs [cell_index]; + } - } + } else if (! info.cell_name.empty ()) { + + std::pair cc = cell_by_name (info.cell_name.c_str ()); + if (cc.first) { + return m_cell_ptrs [cc.second]; + } } @@ -2359,21 +2676,17 @@ Layout::unregister_lib_proxy (db::LibraryProxy *lib_proxy) } void -Layout::get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping) +Layout::get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping, bool retain_layout) { - tl_assert (! (manager () && manager ()->transacting ())); tl_assert (m_cell_ptrs [target_cell_index] != 0); - invalidate_hier (); - - m_cells.erase (iterator (m_cell_ptrs [target_cell_index])); - LibraryProxy *proxy = new LibraryProxy (target_cell_index, *this, lib->get_id (), cell_index); - m_cells.push_back_ptr (proxy); - m_cell_ptrs [target_cell_index] = proxy; + replace_cell (target_cell_index, proxy, retain_layout); - // produce the layout - proxy->update (layer_mapping); + if (! retain_layout) { + // produce the layout unless we retained it + proxy->update (layer_mapping); + } } cell_index_type @@ -2404,7 +2717,7 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index) manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0)); } - // produce the layout + // produce the layout proxy->update (); return new_index; @@ -2412,6 +2725,46 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index) } } +cell_index_type +Layout::create_cold_proxy (const db::ProxyContextInfo &info) +{ + // create a new unique name + std::string b; + if (! info.cell_name.empty ()) { + b = info.cell_name; + } else if (! info.pcell_name.empty ()) { + b = info.pcell_name; + } + if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) { + b = uniquify_cell_name (b.c_str ()); + } + + // create a new cell (a LibraryProxy) + cell_index_type new_index = allocate_new_cell (); + + ColdProxy *proxy = new ColdProxy (new_index, *this, info); + m_cells.push_back_ptr (proxy); + m_cell_ptrs [new_index] = proxy; + + // enter it's index and cell_name + register_cell_name (b.c_str (), new_index); + + if (manager () && manager ()->transacting ()) { + manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0)); + } + + return new_index; +} + +void +Layout::create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type target_cell_index) +{ + tl_assert (m_cell_ptrs [target_cell_index] != 0); + + ColdProxy *proxy = new ColdProxy (target_cell_index, *this, info); + replace_cell (target_cell_index, proxy, true); +} + void Layout::redo (db::Op *op) { diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index f1555cb61..d9c78019e 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -67,6 +67,7 @@ class Region; class Edges; class EdgePairs; class Texts; +class Technology; class CellMapping; class LayerMapping; @@ -456,6 +457,20 @@ public: virtual std::pair map_layer (const LayerProperties &lprops) = 0; }; +/** + * @brief A binary object representing context information for regenerating library proxies and PCells + */ +struct DB_PUBLIC ProxyContextInfo +{ + std::string lib_name; + std::string cell_name; + std::string pcell_name; + std::map pcell_parameters; + + static ProxyContextInfo deserialize (std::vector::const_iterator from, std::vector::const_iterator to); + void serialize (std::vector &strings); +}; + /** * @brief The layout object * @@ -555,10 +570,36 @@ public: } /** - * @brief Clear the layout + * @brief Clears the layout */ void clear (); + /** + * @brief Gets the technology name the layout is associated with + */ + const std::string &technology_name () const + { + return m_tech_name; + } + + /** + * @brief Gets the technology object the layout is associated with or null if no valid technology is associated + */ + const db::Technology *technology () const; + + /** + * @brief Changes the technology, the layout is associated with + * Changing the layout may re-assess all the library references as libraries can be technology specific + */ + void set_technology_name (const std::string &tech); + + /** + * @brief Changes the technology name + * This method will only change the technology name, but does not re-assess the library links. + * It's provided mainly to support undo/redo and testing. + */ + void set_technology_name_without_update (const std::string &tech); + /** * @brief Accessor to the array repository */ @@ -843,8 +884,9 @@ public: * @param parameters The PCell parameters * @param cell_index The cell index which is to be replaced by the PCell variant proxy * @param layer_mapping The optional layer mapping object that maps the PCell layers to the layout's layers + * @param retain_layout Set to true for not using update() on the PCell but to retain existing layout (conservative approach) */ - void get_pcell_variant_as (pcell_id_type pcell_id, const std::vector ¶meters, cell_index_type cell_index, ImportLayerMapping *layer_mapping = 0); + void get_pcell_variant_as (pcell_id_type pcell_id, const std::vector ¶meters, cell_index_type cell_index, ImportLayerMapping *layer_mapping = 0, bool retain_layout = false); /** * @brief Get the PCell variant cell of a existing cell with new parameters @@ -990,9 +1032,21 @@ public: /** * @brief Get the proxy cell (index) for a given library an cell index (inside that library) * - * This method replaces the cell with the given target cell index by a library. + * @param retain_layout Set to true for not using update() on the PCell but to retain existing layout (conservative approach) + * + * This method replaces the cell with the given target cell index by a library. */ - void get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping = 0); + void get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping = 0, bool retain_layout = false); + + /** + * @brief Creates a cold proxy representing the given context information + */ + cell_index_type create_cold_proxy (const db::ProxyContextInfo &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); /** * @brief Get the context information for a given cell (for writing into a file) @@ -1004,6 +1058,11 @@ public: */ bool get_context_info (cell_index_type cell_index, std::vector &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; + /** * @brief Recover a proxy cell from the given context info. * @@ -1015,6 +1074,11 @@ public: */ db::Cell *recover_proxy (std::vector ::const_iterator from, std::vector ::const_iterator to); + /** + * @brief Recover a proxy cell from the given binary context info object. + */ + db::Cell *recover_proxy (const ProxyContextInfo &context_info); + /** * @brief Recover a proxy cell from the given context info. * @@ -1030,6 +1094,27 @@ public: */ bool recover_proxy_as (cell_index_type cell_index, std::vector ::const_iterator from, std::vector ::const_iterator to, ImportLayerMapping *layer_mapping = 0); + /** + * @brief Recover a proxy cell from the given binary context info object + * + * 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); + + /** + * @brief Restores proxies as far as possible + * + * This feature can be used after a library update to make sure that proxies are updated. + * Library updates may enabled lost connections which are help in cold proxies. This method will recover + * these connections. + */ + void restore_proxies(ImportLayerMapping *layer_mapping = 0); + + /** + * @brief Replaces the given cell index with the new cell + */ + void replace_cell (cell_index_type target_cell_index, db::Cell *new_cell, bool retain_layout); + /** * @brief Delete a cell plus the subcells not used otherwise * @@ -1738,6 +1823,11 @@ public: */ const std::string &meta_info_value (const std::string &name) const; + /** + * @brief This event is triggered when the technology changes + */ + tl::Event technology_changed_event; + protected: /** * @brief Establish the graph's internals according to the dirty flags @@ -1779,6 +1869,7 @@ private: bool m_do_cleanup; bool m_editable; meta_info m_meta_info; + std::string m_tech_name; tl::Mutex m_lock; /** @@ -1832,6 +1923,11 @@ private: * @brief Implementation of prune_cells and some prune_subcells variants */ void do_prune_cells_or_subcells (const std::set &ids, int levels, bool subcells); + + /** + * @brief Recovers a proxy without considering the library from context_info + */ + db::Cell *recover_proxy_no_lib (const ProxyContextInfo &context_info); }; /** diff --git a/src/db/db/dbLayoutContextHandler.cc b/src/db/db/dbLayoutContextHandler.cc index 5c6bf49fb..90cff6c50 100644 --- a/src/db/db/dbLayoutContextHandler.cc +++ b/src/db/db/dbLayoutContextHandler.cc @@ -80,7 +80,7 @@ tl::Variant LayoutContextHandler::eval_double_bracket (const std::string &s) con std::string tail = cp + 1; - db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (libname); + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (libname, mp_layout->technology_name ()); if (! lib) { throw tl::Exception (tl::to_string (tr ("Not a valid library name: ")) + libname); } diff --git a/src/db/db/dbLayoutQuery.cc b/src/db/db/dbLayoutQuery.cc index 27ed3d8ad..3cf34a18e 100644 --- a/src/db/db/dbLayoutQuery.cc +++ b/src/db/db/dbLayoutQuery.cc @@ -228,7 +228,7 @@ public: { // get the layers which we have to look for for (db::Layout::layer_iterator l = layout->begin_layers (); l != layout->end_layers (); ++l) { - if (layers.is_empty () || layers.logical (*(*l).second).first) { + if (layers.is_empty () || layers.is_mapped (*(*l).second)) { m_layers.push_back ((*l).first); } } diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index 0e10fe3e9..c22dff473 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -191,11 +191,10 @@ Library::remap_to (db::Library *other) if (! pn.first) { - // substitute by static layout cell - std::string name = r->first->cell_name (ci); - db::Cell *old_cell = r->first->take_cell (ci); - r->first->insert_cell (ci, name, new db::Cell (*old_cell)); - delete old_cell; + // substitute by a cold proxy + db::ProxyContextInfo info; + r->first->get_context_info (ci, info); + r->first->create_cold_proxy_as (info, ci); } else { @@ -203,11 +202,10 @@ Library::remap_to (db::Library *other) const db::PCellDeclaration *new_pcell_decl = other->layout ().pcell_declaration (pn.second); if (! old_pcell_decl || ! new_pcell_decl) { - // substitute by static layout cell - std::string name = r->first->cell_name (ci); - db::Cell *old_cell = r->first->take_cell (ci); - r->first->insert_cell (ci, name, new db::Cell (*old_cell)); - delete old_cell; + // substitute by a cold proxy + db::ProxyContextInfo info; + r->first->get_context_info (ci, info); + r->first->create_cold_proxy_as (info, ci); } else { @@ -232,11 +230,10 @@ Library::remap_to (db::Library *other) if (! cn.first) { - // unlink this proxy: substitute by static layout cell - std::string name = r->first->cell_name (ci); - db::Cell *old_cell = r->first->take_cell (ci); - r->first->insert_cell (ci, name, new db::Cell (*old_cell)); - delete old_cell; + // substitute by a cold proxy + db::ProxyContextInfo info; + r->first->get_context_info (ci, info); + r->first->create_cold_proxy_as (info, ci); } else { diff --git a/src/db/db/dbLibraryManager.cc b/src/db/db/dbLibraryManager.cc index ba8f2b685..2404f1217 100644 --- a/src/db/db/dbLibraryManager.cc +++ b/src/db/db/dbLibraryManager.cc @@ -24,6 +24,7 @@ #include "dbLibraryManager.h" #include "dbLibrary.h" #include "dbCommon.h" +#include "dbColdProxy.h" #include "tlAssert.h" #include "tlStaticObjects.h" @@ -66,14 +67,39 @@ LibraryManager::~LibraryManager () } std::pair -LibraryManager::lib_by_name (const std::string &name) const +LibraryManager::lib_by_name (const std::string &name, const std::set &for_technologies) const { - iterator l = m_lib_by_name.find (name); - if (l == m_lib_by_name.end ()) { - return std::make_pair (false, lib_id_type (0)); - } else { - return std::make_pair (true, l->second); + iterator l; + + if (! for_technologies.empty ()) { + + l = m_lib_by_name.find (name); + while (l != m_lib_by_name.end () && l->first == name) { + const db::Library *lptr = lib (l->second); + bool found = lptr->for_technologies (); + for (std::set::const_iterator t = for_technologies.begin (); t != for_technologies.end () && found; ++t) { + if (! lptr->is_for_technology (*t)) { + found = false; + } + } + if (found) { + return std::make_pair (true, l->second); + } + ++l; + } + } + + // fallback: technology-unspecific libs + l = m_lib_by_name.find (name); + while (l != m_lib_by_name.end () && l->first == name) { + if (! lib (l->second)->for_technologies ()) { + return std::make_pair (true, l->second); + } + ++l; + } + + return std::make_pair (false, lib_id_type (0)); } void @@ -116,15 +142,44 @@ LibraryManager::register_lib (Library *library) library->set_id (id); // if the new library replaces the old one, remap existing library proxies before deleting the library - lib_name_map::iterator ln = m_lib_by_name.find (library->get_name ()); - if (ln != m_lib_by_name.end () && m_libs [ln->second]) { - m_libs [ln->second]->remap_to (library); - delete m_libs [ln->second]; - m_libs [ln->second] = 0; + // (replacement is done only when all technologies are substituted) + lib_name_map::iterator l = m_lib_by_name.find (library->get_name ()); + bool found = false; + while (l != m_lib_by_name.end () && l->first == library->get_name ()) { + if (m_libs [l->second] && m_libs [l->second]->get_technologies () == library->get_technologies ()) { + found = true; + break; + } + ++l; } - m_lib_by_name.insert (std::make_pair (library->get_name (), id)).first->second = id; + if (found) { + // substitute + m_libs [l->second]->remap_to (library); + delete m_libs [l->second]; + m_libs [l->second] = 0; + m_lib_by_name.erase (l); + } + // insert new lib as first of this name + l = m_lib_by_name.find (library->get_name ()); + m_lib_by_name.insert (l, std::make_pair (library->get_name (), id)); + + // take care of cold referrers - these may not get valid + // NOTE: this will try to substitute the cold proxies we may have generated during "remap_to" above, but + // "restore_proxies" takes care not to re-substitute cold proxies. + + const tl::weak_collection &cold_proxies = db::ColdProxy::cold_proxies_per_lib_name (library->get_name ()); + std::set to_refresh; + for (tl::weak_collection::const_iterator p = cold_proxies.begin (); p != cold_proxies.end (); ++p) { + to_refresh.insert (const_cast (p->layout ())); + } + + for (std::set::const_iterator l = to_refresh.begin (); l != to_refresh.end (); ++l) { + (*l)->restore_proxies (0); + } + + // issue the change notification changed_event (); return id; diff --git a/src/db/db/dbLibraryManager.h b/src/db/db/dbLibraryManager.h index 48cf5e0a1..34d9e4428 100644 --- a/src/db/db/dbLibraryManager.h +++ b/src/db/db/dbLibraryManager.h @@ -33,6 +33,7 @@ #include #include #include +#include namespace db { @@ -50,7 +51,7 @@ class Library; class DB_PUBLIC LibraryManager { public: - typedef std::map lib_name_map; + typedef std::multimap lib_name_map; typedef lib_name_map::const_iterator iterator; /** @@ -90,11 +91,41 @@ public: } /** - * @brief Get the library by name + * @brief Get the library by name which is valid for all given technologies + * + * This method looks up a library which is valid for all technologies listed in "for_technologies". It may be + * responsible for more than that too. * * @return A pair, the boolean is true, if the name is valid. The second member is the library id. */ - std::pair lib_by_name (const std::string &name) const; + std::pair lib_by_name (const std::string &name, const std::set &for_technologies) const; + + /** + * @brief Get the library by name which is valid for the given technology + * + * This method looks up a library which is valid for the given technology. It may be + * responsible for more than that too. + * + * @return A pair, the boolean is true, if the name is valid. The second member is the library id. + */ + std::pair lib_by_name (const std::string &name, const std::string &for_technology) const + { + std::set techs; + if (! for_technology.empty ()) { + techs.insert (for_technology); + } + return lib_by_name (name, techs); + } + + /** + * @brief Get the library by name for any technology + * + * @return A pair, the boolean is true, if the name is valid. The second member is the library id. + */ + std::pair lib_by_name (const std::string &name) const + { + return lib_by_name (name, std::set ()); + } /** * @brief Get the library by name @@ -111,6 +142,36 @@ public: } } + /** + * @brief Get the library by name and technology + * + * @return The pointer to the library or 0, if there is no library with that name. + */ + Library *lib_ptr_by_name (const std::string &name, const std::string &for_technology) const + { + std::pair ll = lib_by_name (name, for_technology); + if (ll.first) { + return lib (ll.second); + } else { + return 0; + } + } + + /** + * @brief Get the library by name and technology + * + * @return The pointer to the library or 0, if there is no library with that name. + */ + Library *lib_ptr_by_name (const std::string &name, const std::set &for_technologies) const + { + std::pair ll = lib_by_name (name, for_technologies); + if (ll.first) { + return lib (ll.second); + } else { + return 0; + } + } + /** * @brief Register a library under the given name and associate a id * diff --git a/src/db/db/dbLibraryProxy.cc b/src/db/db/dbLibraryProxy.cc index 195c82b99..241df85ad 100644 --- a/src/db/db/dbLibraryProxy.cc +++ b/src/db/db/dbLibraryProxy.cc @@ -99,8 +99,6 @@ LibraryProxy::remap (lib_id_type lib_id, cell_index_type lib_cell_index) m_lib_id = lib_id; m_library_cell_index = lib_cell_index; - // It's important to register at the new library, but the old library is about to the deleted, so we don't unregister. - // That does not disturb the old library iterating over the layouts. db::Library *lib = db::LibraryManager::instance ().lib (m_lib_id); if (lib) { lib->register_proxy (this, layout ()); diff --git a/src/db/db/dbManager.cc b/src/db/db/dbManager.cc index 5e53b1a83..0a9e24016 100644 --- a/src/db/db/dbManager.cc +++ b/src/db/db/dbManager.cc @@ -156,16 +156,14 @@ Manager::cancel () m_opened = false; if (m_current->first.begin () != m_current->first.end ()) { - ++m_current; undo (); - - } else { - // empty transactions .. just delete - erase_transactions (m_current, m_transactions.end ()); - m_current = m_transactions.end (); } + // wipe following history as we don't want the cancelled operation to be redoable + erase_transactions (m_current, m_transactions.end ()); + m_current = m_transactions.end (); + } } diff --git a/src/db/db/dbNamedLayerReader.cc b/src/db/db/dbNamedLayerReader.cc index 497fad153..c9fce8438 100644 --- a/src/db/db/dbNamedLayerReader.cc +++ b/src/db/db/dbNamedLayerReader.cc @@ -122,21 +122,38 @@ extract_ld (const char *s, int &l, int &d, std::string &n) std::pair NamedLayerReader::open_layer (db::Layout &layout, const std::string &n) +{ + return open_layer (layout, n, keep_layer_names (), create_layers ()); +} + +std::pair +NamedLayerReader::open_layer (db::Layout &layout, const std::string &n, bool keep_layer_name, bool create_layer) +{ + std::map >::const_iterator lc = m_layer_cache.find (n); + if (lc != m_layer_cache.end ()) { + return lc->second; + } else { + std::pair res = open_layer_uncached (layout, n, keep_layer_name, create_layer); + m_layer_cache.insert (std::make_pair (n, res)); + return res; + } +} + +std::pair +NamedLayerReader::open_layer_uncached (db::Layout &layout, const std::string &n, bool keep_layer_name, bool create_layer) { int l = -1, d = -1; std::string on; - std::pair ll (false, 0); - - ll = m_layer_map.logical (n, layout); - if (! ll.first && !m_keep_layer_names) { + std::set li = m_layer_map.logical (n, layout); + if (li.empty () && ! keep_layer_name) { if (extract_plain_layer (n.c_str (), l)) { db::LayerProperties lp; lp.layer = l; lp.datatype = 0; - ll = m_layer_map.logical (lp, layout); + li = m_layer_map.logical (lp, layout); } else if (extract_ld (n.c_str (), l, d, on)) { @@ -144,22 +161,35 @@ NamedLayerReader::open_layer (db::Layout &layout, const std::string &n) lp.layer = l; lp.datatype = d; lp.name = on; - ll = m_layer_map.logical (lp, layout); + li = m_layer_map.logical (lp, layout); } } - if (ll.first) { + if (! li.empty ()) { - // create the layer if it is not part of the layout yet. - if (! layout.is_valid_layer (ll.second)) { - layout.insert_layer (ll.second, m_layer_map.mapping (ll.second)); + for (std::set::const_iterator i = li.begin (); i != li.end (); ++i) { + m_layer_map_out.mmap (n, *i, layout.get_properties (*i)); } - return ll; + if (li.size () == 1) { - } else if (! m_create_layers) { + return std::make_pair (true, *li.begin ()); + + } else { + + std::map, unsigned int>::iterator mmp = m_multi_mapping_placeholders.find (li); + if (mmp == m_multi_mapping_placeholders.end ()) { + // create a placeholder layer for later + mmp = m_multi_mapping_placeholders.insert (std::make_pair (li, layout.insert_layer ())).first; + } + + return std::make_pair (true, mmp->second); + + } + + } else if (! create_layer) { return std::pair (false, 0); @@ -188,21 +218,53 @@ NamedLayerReader::open_layer (db::Layout &layout, const std::string &n) void NamedLayerReader::map_layer (const std::string &name, unsigned int layer) { - m_layer_map.map (name, layer); + m_layer_cache [name] = std::make_pair (true, layer); + m_layer_map_out.map (name, layer); } void -NamedLayerReader::prepare_layers () +NamedLayerReader::prepare_layers (db::Layout &layout) { m_new_layers.clear (); m_next_layer_index = m_layer_map.next_index (); + + m_layer_map_out.clear (); + m_multi_mapping_placeholders.clear (); + m_layer_cache.clear (); + + m_layer_map.prepare (layout); } void NamedLayerReader::finish_layers (db::Layout &layout) { + // resolve layer multi-mapping + + for (std::map, unsigned int>::const_iterator i = m_multi_mapping_placeholders.begin (); i != m_multi_mapping_placeholders.end (); ++i) { + + if (i->first.size () > 1) { + + bool discard_layer = i->first.find (i->second) == i->first.end (); + + for (std::set::const_iterator l = i->first.begin (); l != i->first.end (); ++l) { + + // last one? this one will get a "move" + std::set::const_iterator ll = l; + if (discard_layer && ++ll == i->first.end ()) { + layout.move_layer (i->second, *l); + layout.delete_layer (i->second); + } else { + layout.copy_layer (i->second, *l); + } + + } + + } + + } + // assign layer numbers to new layers - if (! m_new_layers.empty () && !m_keep_layer_names) { + if (! m_new_layers.empty () && ! m_keep_layer_names) { std::set > used_ld; for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { diff --git a/src/db/db/dbNamedLayerReader.h b/src/db/db/dbNamedLayerReader.h index d3b677111..5bf234b47 100644 --- a/src/db/db/dbNamedLayerReader.h +++ b/src/db/db/dbNamedLayerReader.h @@ -54,6 +54,7 @@ public: */ NamedLayerReader (); +protected: /** * @brief Sets a value indicating whether to create new layers */ @@ -73,13 +74,21 @@ public: void set_layer_map (const LayerMap &lm); /** - * @brief Gets the layer map + * @brief Gets the input layer map */ const LayerMap &layer_map () { return m_layer_map; } + /** + * @brief Gets the layer map + */ + const LayerMap &layer_map_out () + { + return m_layer_map_out; + } + /** * @brief Sets a value indicating whether layer names are kept * If set to true, no name translation is performed and layers are @@ -96,7 +105,6 @@ public: return m_keep_layer_names; } -protected: /** * @brief Opens a new layer * This method will create or locate a layer for a given name. @@ -105,6 +113,14 @@ protected: */ std::pair open_layer (db::Layout &layout, const std::string &name); + /** + * @brief Opens a new layer + * This method will create or locate a layer for a given name. + * The result's first attribute is true, if such a layer could be found + * or created. In this case, the second attribute is the layer index. + */ + std::pair open_layer (db::Layout &layout, const std::string &name, bool keep_layer_name, bool create_layer); + /** * @brief Force mapping of a name to a layer index */ @@ -121,7 +137,7 @@ protected: * @brief Prepares reading * This method must be called before the reading is done. */ - void prepare_layers (); + void prepare_layers (db::Layout &layout); private: bool m_create_layers; @@ -129,6 +145,11 @@ private: LayerMap m_layer_map; unsigned int m_next_layer_index; std::map m_new_layers; + db::LayerMap m_layer_map_out; + std::map > m_layer_cache; + std::map, unsigned int> m_multi_mapping_placeholders; + + std::pair open_layer_uncached (db::Layout &layout, const std::string &name, bool keep_layer_name, bool create_layer); }; } diff --git a/src/db/db/dbPCellDeclaration.h b/src/db/db/dbPCellDeclaration.h index 540efa369..e27ad00b0 100644 --- a/src/db/db/dbPCellDeclaration.h +++ b/src/db/db/dbPCellDeclaration.h @@ -29,6 +29,7 @@ #include "gsiObject.h" #include "dbLayout.h" #include "tlVariant.h" +#include "tlObject.h" namespace db { @@ -327,7 +328,8 @@ public: * @brief A declaration for a PCell */ class DB_PUBLIC PCellDeclaration - : public gsi::ObjectBase + : public gsi::ObjectBase, + public tl::Object { public: /** diff --git a/src/db/db/dbReader.cc b/src/db/db/dbReader.cc index fc6f13adb..2ac8add6b 100644 --- a/src/db/db/dbReader.cc +++ b/src/db/db/dbReader.cc @@ -30,6 +30,33 @@ namespace db { +// --------------------------------------------------------------- + +DB_PUBLIC void +join_layer_names (std::string &s, const std::string &n) +{ + if (s == n) { + return; + } + + if (! s.empty ()) { + + size_t i = s.find (n); + if (i != std::string::npos && (i == 0 || s.c_str ()[i - 1] == ';')) { + char after = s.c_str ()[i + n.size ()]; + if (after == 0 || after == ';') { + // n is already contained in s + return; + } + } + + s += ";"; + + } + + s += n; +} + // --------------------------------------------------------------- // ReaderBase implementation diff --git a/src/db/db/dbReader.h b/src/db/db/dbReader.h index bab39df87..65c2ebf0d 100644 --- a/src/db/db/dbReader.h +++ b/src/db/db/dbReader.h @@ -54,6 +54,14 @@ public: { } }; +/** + * @brief Joins layer names into a single, combined layer + * @param s The first layer name and output + * @param n The name to add + */ +DB_PUBLIC void +join_layer_names (std::string &s, const std::string &n); + /** * @brief The generic reader base class */ diff --git a/src/db/db/dbStreamLayers.cc b/src/db/db/dbStreamLayers.cc index fb33f3ac7..52c14a679 100644 --- a/src/db/db/dbStreamLayers.cc +++ b/src/db/db/dbStreamLayers.cc @@ -40,10 +40,9 @@ namespace db /// A helper class to join two datatype map members struct LmapJoinOp1 { - void operator() (unsigned int &a, unsigned int b) + void operator() (std::set &a, const std::set &b) { - // TODO: could also do?: throw an exception .. - a = b; + a.insert (b.begin (), b.end ()); } }; @@ -58,7 +57,27 @@ struct LmapJoinOp2 } }; -/// Utility typedefs for the expression parser +/// A helper class to implement the unmap operation +struct LmapEraseDatatypeInterval +{ + LmapEraseDatatypeInterval (unsigned int dfrom, unsigned int dto) + : m_dfrom (dfrom), m_dto (dto) + { } + + void operator() (LayerMap::datatype_map &a, const LayerMap::datatype_map &) + { + if (is_static_ld (m_dfrom) && is_static_ld (m_dto)) { + a.erase (m_dfrom, m_dto + 1); + } else { + a.clear (); + } + } + +private: + unsigned int m_dfrom, m_dto; +}; + +/// Utility typedefs for the expression parser typedef std::pair ld_interval; /// Utility typedefs for the expression parser @@ -71,70 +90,104 @@ LayerMap::LayerMap () // .. nothing yet .. } -std::pair +bool +LayerMap::is_mapped (const LDPair &p) const +{ + const datatype_map *dm = m_ld_map.mapped (p.layer); + if (!dm) { + return false; + } + const std::set *l = dm->mapped (p.datatype); + return (l && ! l->empty ()); +} + +bool +LayerMap::is_mapped (const std::string &n) const +{ + std::map >::const_iterator m = m_name_map.find (n); + return m != m_name_map.end () && ! m->second.empty (); +} + +bool +LayerMap::is_mapped (const db::LayerProperties &p) const +{ + std::set m; + if (p.layer >= 0 && p.datatype >= 0) { + if (is_mapped (db::LDPair (p.layer, p.datatype))) { + return true; + } + } + if (! p.name.empty ()) { + return is_mapped (p.name); + } else { + return false; + } +} + +std::set LayerMap::logical (const LDPair &p) const { return logical_internal (p, false); } -std::pair +std::set LayerMap::logical (const std::string &n) const { return logical_internal (n, false); } -std::pair +std::set LayerMap::logical (const db::LayerProperties &p) const { return logical_internal (p, false); } -std::pair +std::set LayerMap::logical_internal (const LDPair &p, bool allow_placeholder) const { const datatype_map *dm = m_ld_map.mapped (p.layer); if (dm) { - const unsigned int *l = dm->mapped (p.datatype); + const std::set *l = dm->mapped (p.datatype); if (l && (allow_placeholder || ! is_placeholder (*l))) { - return std::make_pair (true, *l); + return *l; } } - return std::make_pair (false, 0); + return std::set (); } -std::pair +std::set LayerMap::logical_internal (const std::string &n, bool allow_placeholder) const { - std::map::const_iterator m = m_name_map.find (n); + std::map >::const_iterator m = m_name_map.find (n); if (m != m_name_map.end () && (allow_placeholder || ! is_placeholder (m->second))) { - return std::make_pair (true, m->second); + return m->second; } else { - return std::make_pair (false, 0); + return std::set (); } } -std::pair +std::set LayerMap::logical_internal (const db::LayerProperties &p, bool allow_placeholder) const { + std::set m; if (p.layer >= 0 && p.datatype >= 0) { - std::pair m = logical_internal (db::LDPair (p.layer, p.datatype), allow_placeholder); - if (m.first) { - return m; - } + m = logical_internal (db::LDPair (p.layer, p.datatype), allow_placeholder); } - if (! p.name.empty ()) { - std::pair m = logical_internal (p.name, allow_placeholder); - if (m.first) { - return m; - } + if (m.empty () && ! p.name.empty ()) { + m = logical_internal (p.name, allow_placeholder); } - return std::make_pair (false, 0); + return m; } bool -LayerMap::is_placeholder (unsigned int l) const +LayerMap::is_placeholder (const std::set &m) const { - return (m_placeholders.size () > std::numeric_limits::max () - l); + for (std::set::const_iterator i = m.begin (); i != m.end (); ++i) { + if (m_placeholders.size () > std::numeric_limits::max () - *i) { + return true; + } + } + return false; } const db::LayerProperties * @@ -148,39 +201,52 @@ LayerMap::target (unsigned int l) const } } -std::pair +std::set LayerMap::logical (const db::LayerProperties &p, db::Layout &layout) const { - std::pair l = logical_internal (p, true); - if (l.first && is_placeholder (l.second)) { - return const_cast (this)->substitute_placeholder (p, l.second, layout); + std::set l = logical_internal (p, true); + if (is_placeholder (l)) { + return const_cast (this)->substitute_placeholder (p, l, layout); } else { return l; } } -std::pair +std::set LayerMap::logical (const db::LDPair &p, db::Layout &layout) const { - std::pair l = logical_internal (p, true); - if (l.first && is_placeholder (l.second)) { - return const_cast (this)->substitute_placeholder (db::LayerProperties (p.layer, p.datatype), l.second, layout); + std::set l = logical_internal (p, true); + if (is_placeholder (l)) { + return const_cast (this)->substitute_placeholder (db::LayerProperties (p.layer, p.datatype), l, layout); } else { return l; } } -std::pair -LayerMap::substitute_placeholder (const db::LayerProperties &p, unsigned int ph, db::Layout &layout) +std::set +LayerMap::substitute_placeholder (const db::LayerProperties &p, const std::set &m, db::Layout &layout) { - const db::LayerProperties &lp_ph = m_placeholders [std::numeric_limits::max () - ph]; - db::LayerProperties lp_new = p; - lp_new.layer = db::ld_combine (p.layer, lp_ph.layer); - lp_new.datatype = db::ld_combine (p.datatype, lp_ph.datatype); + std::set res; + for (std::set::const_iterator i = m.begin (); i != m.end (); ++i) { - unsigned int l_new = layout.insert_layer (lp_new); - map (p, l_new, lp_new); - return std::make_pair (true, l_new); + if (m_placeholders.size () > std::numeric_limits::max () - *i) { + + const db::LayerProperties &lp_ph = m_placeholders [std::numeric_limits::max () - *i]; + db::LayerProperties lp_new = p; + lp_new.layer = db::ld_combine (p.layer, lp_ph.layer); + lp_new.datatype = db::ld_combine (p.datatype, lp_ph.datatype); + + unsigned int l_new = layout.insert_layer (lp_new); + map (p, l_new, lp_new); + res.insert (l_new); + + } else { + res.insert (*i); + } + + } + + return res; } static std::string format_interval (ld_type l1, ld_type l2) @@ -196,44 +262,93 @@ static std::string format_interval (ld_type l1, ld_type l2) } } +static std::vector > +extract_dt_intervals (const LayerMap::datatype_map &dt_map, int ll, bool &has_others) +{ + std::vector > res; + + for (LayerMap::datatype_map::const_iterator d = dt_map.begin (); d != dt_map.end (); ) { + + if (d->second.find (ll) != d->second.end ()) { + + std::pair dpi = d->first; + + if (d->second.size () > 1) { + has_others = true; + } + + LayerMap::datatype_map::const_iterator dd = d; + ++dd; + while (dd != dt_map.end () && dd->first.first == dpi.second && dd->second.find (ll) != dd->second.end ()) { + if (dd->second.size () > 1) { + has_others = true; + } + dpi.second = dd->first.second; + ++dd; + } + + d = dd; + + res.push_back (dpi); + + } else { + ++d; + } + + } + return res; +} + std::string LayerMap::mapping_str (unsigned int ll) const { std::string s; bool f1 = true; + bool is_mmap = false; - for (ld_map::const_iterator l = m_ld_map.begin (); l != m_ld_map.end (); ++l) { + for (ld_map::const_iterator l = m_ld_map.begin (); l != m_ld_map.end (); ) { + + std::pair lti = l->first; + + std::vector > dti = extract_dt_intervals (l->second, ll, is_mmap); + ++l; + while (l != m_ld_map.end () && lti.second == l->first.first && extract_dt_intervals (l->second, ll, is_mmap) == dti) { + lti.second = l->first.second; + ++l; + } bool f2 = true; - for (datatype_map::const_iterator d = l->second.begin (); d != l->second.end (); ++d) { - if (d->second == ll) { + for (std::vector >::const_iterator d = dti.begin (); d != dti.end (); ++d) { - // create a string representation - if (!f2) { - s += ","; - } else { - - if (!f1) { - s += ";"; - } - f1 = false; - - s += format_interval (l->first.first, l->first.second); - s += "/"; + // create a string representation + if (!f2) { + s += ","; + } else { + if (!f1) { + s += ";"; } - f2 = false; + f1 = false; - s += format_interval (d->first.first, d->first.second); + s += format_interval (lti.first, lti.second); + s += "/"; } + f2 = false; + + s += format_interval (d->first, d->second); } } - for (std::map ::const_iterator l = m_name_map.begin (); l != m_name_map.end (); ++l) { - if (l->second == ll) { + for (std::map >::const_iterator l = m_name_map.begin (); l != m_name_map.end (); ++l) { + + if (l->second.find (ll) != l->second.end ()) { + + if (l->second.size () > 1) { + is_mmap = true; + } if (!f1) { s += ";"; @@ -243,6 +358,7 @@ LayerMap::mapping_str (unsigned int ll) const s += tl::to_word_or_quoted_string (l->first); } + } std::map::const_iterator t = m_target_layers.find (ll); @@ -251,7 +367,11 @@ LayerMap::mapping_str (unsigned int ll) const s += t->second.to_string (true); } - return s; + if (is_mmap) { + return "+" + s; + } else { + return s; + } } void @@ -300,11 +420,19 @@ LayerMap::prepare (db::Layout &layout) // Now remap the indexes for (ld_map::iterator l = m_ld_map.begin (); l != m_ld_map.end (); ++l) { for (datatype_map::iterator d = l->second.begin (); d != l->second.end (); ++d) { - d->second = real_layers [d->second]; + std::set dn; + for (std::set::const_iterator i = d->second.begin (); i != d->second.end (); ++i) { + dn.insert (real_layers [*i]); + } + d->second = dn; } } - for (std::map::iterator n = m_name_map.begin (); n != m_name_map.end (); ++n) { - n->second = real_layers [n->second]; + for (std::map >::iterator n = m_name_map.begin (); n != m_name_map.end (); ++n) { + std::set dn; + for (std::set::const_iterator i = n->second.begin (); i != n->second.end (); ++i) { + dn.insert (real_layers [*i]); + } + n->second = dn; } std::map old_target_layers; @@ -329,11 +457,11 @@ LayerMap::get_layers () const for (ld_map::const_iterator l = m_ld_map.begin (); l != m_ld_map.end (); ++l) { for (datatype_map::const_iterator d = l->second.begin (); d != l->second.end (); ++d) { - layers.insert (d->second); + layers.insert (d->second.begin (), d->second.end ()); } } for (const_iterator_names n = m_name_map.begin (); n != m_name_map.end (); ++n) { - layers.insert(n->second); + layers.insert(n->second.begin (), n->second.end ()); } return std::vector (layers.begin (), layers.end ()); @@ -358,7 +486,7 @@ LayerMap::mapping (unsigned int ll) const // no mapping is given. Use the lowest layer and datatype for (ld_map::const_iterator l = m_ld_map.begin (); l != m_ld_map.end (); ++l) { for (datatype_map::const_iterator d = l->second.begin (); d != l->second.end (); ++d) { - if (d->second == ll) { + if (d->second.find (ll) != d->second.end ()) { p.layer = l->first.first; p.datatype = d->first.first; break; @@ -373,7 +501,7 @@ LayerMap::mapping (unsigned int ll) const // no mapping is given. Use the lowest layer and datatype for (ld_map::const_iterator l = m_ld_map.begin (); l != m_ld_map.end (); ++l) { for (datatype_map::const_iterator d = l->second.begin (); d != l->second.end (); ++d) { - if (d->second == ll) { + if (d->second.find (ll) != d->second.end ()) { p.layer = l->first.first; p.datatype = d->first.first; break; @@ -384,8 +512,8 @@ LayerMap::mapping (unsigned int ll) const } if (p.name.empty ()) { - for (std::map ::const_iterator l = m_name_map.begin (); l != m_name_map.end (); ++l) { - if (l->second == ll) { + for (std::map >::const_iterator l = m_name_map.begin (); l != m_name_map.end (); ++l) { + if (l->second.find (ll) != l->second.end ()) { p.name = l->first; break; } @@ -397,59 +525,59 @@ LayerMap::mapping (unsigned int ll) const } void -LayerMap::map (const LDPair &p, unsigned int l) +LayerMap::mmap (const LDPair &p, unsigned int l) { insert (p, p, l, (const LayerProperties *) 0); } void -LayerMap::map (const std::string &name, unsigned int l) +LayerMap::mmap (const std::string &name, unsigned int l) { insert (name, l, (const LayerProperties *) 0); } void -LayerMap::map (const LayerProperties &f, unsigned int l) +LayerMap::mmap (const LayerProperties &f, unsigned int l) { if (f.name.empty () || is_static_ld (f.layer) || is_static_ld (f.datatype)) { - map (db::LDPair (f.layer, f.datatype), l); + mmap (db::LDPair (f.layer, f.datatype), l); } if (! f.name.empty ()) { - map (f.name, l); + mmap (f.name, l); } } void -LayerMap::map (const LDPair &p, unsigned int l, const LayerProperties &t) +LayerMap::mmap (const LDPair &p, unsigned int l, const LayerProperties &t) { insert (p, p, l, &t); } void -LayerMap::map (const std::string &name, unsigned int l, const LayerProperties &t) +LayerMap::mmap (const std::string &name, unsigned int l, const LayerProperties &t) { insert (name, l, &t); } void -LayerMap::map (const LayerProperties &f, unsigned int l, const LayerProperties &t) +LayerMap::mmap (const LayerProperties &f, unsigned int l, const LayerProperties &t) { if (f.name.empty () || is_static_ld (f.layer) || is_static_ld (f.datatype)) { - map (db::LDPair (f.layer, f.datatype), l, t); + mmap (db::LDPair (f.layer, f.datatype), l, t); } if (! f.name.empty ()) { - map (f.name, l, t); + mmap (f.name, l, t); } } void -LayerMap::map (const LDPair &p1, const LDPair &p2, unsigned int l) +LayerMap::mmap (const LDPair &p1, const LDPair &p2, unsigned int l) { insert (p1, p2, l, (const LayerProperties *) 0); } void -LayerMap::map (const LDPair &p1, const LDPair &p2, unsigned int l, const LayerProperties &lp) +LayerMap::mmap (const LDPair &p1, const LDPair &p2, unsigned int l, const LayerProperties &lp) { insert (p1, p2, l, &lp); } @@ -495,18 +623,25 @@ parse_intervals (tl::Extractor &ex, ld_interval_vector &v) } void -LayerMap::map_expr (const std::string &expr, unsigned int l) +LayerMap::mmap_expr (const std::string &expr, unsigned int l) { tl::Extractor ex (expr.c_str ()); - map_expr (ex, l); + mmap_expr (ex, l); ex.expect_end (); } void -LayerMap::map_expr (tl::Extractor &ex, unsigned int l) +LayerMap::mmap_expr (tl::Extractor &ex, unsigned int l) { try { + bool round_bracket = false, square_bracket = false; + if (ex.test ("(")) { + round_bracket = true; + } else if (ex.test ("[")) { + square_bracket = true; + } + do { tl::Extractor ex_saved = ex; @@ -515,7 +650,7 @@ LayerMap::map_expr (tl::Extractor &ex, unsigned int l) ld_type n; if (! ex.try_read (n) && ex.try_read_word_or_quoted (name)) { - m_name_map.insert (std::make_pair (name, l)); + m_name_map [name].insert (l); } else { @@ -533,7 +668,9 @@ LayerMap::map_expr (tl::Extractor &ex, unsigned int l) datatype_map dm; for (ld_interval_vector::const_iterator di = vd.begin (); di != vd.end (); ++di) { LmapJoinOp1 op1; - dm.add (di->first, di->second + 1, l, op1); + std::set single; + single.insert (l); + dm.add (di->first, di->second + 1, single, op1); } for (ld_interval_vector::const_iterator li = vl.begin (); li != vl.end (); ++li) { LmapJoinOp2 op2; @@ -548,7 +685,15 @@ LayerMap::map_expr (tl::Extractor &ex, unsigned int l) LayerProperties lp; lp.read (ex, true); m_target_layers[l] = lp; - } + } else if (square_bracket) { + m_target_layers[l] = LayerProperties (db::any_ld (), db::any_ld ()); + } + + if (round_bracket) { + ex.expect (")"); + } else if (square_bracket) { + ex.expect ("]"); + } } catch (...) { throw LayerSpecFormatException (ex.skip ()); @@ -566,7 +711,7 @@ LayerMap::insert (const std::string &name, unsigned int l, const LayerProperties m_target_layers[l] = *target; } - m_name_map.insert (std::make_pair (name, l)); + m_name_map [name].insert (l); if (l >= m_next_index) { m_next_index = l + 1; @@ -580,13 +725,16 @@ LayerMap::insert (const LDPair &p1, const LDPair &p2, unsigned int l, const Laye m_target_layers[l] = *target; } + std::set single; + single.insert (l); + // create a single-interval list for the datatype range LayerMap::datatype_map dt; LmapJoinOp1 op1; if (db::is_static_ld (p1.datatype) && db::is_static_ld (p2.datatype)) { - dt.add (p1.datatype, p2.datatype + 1, l, op1); + dt.add (p1.datatype, p2.datatype + 1, single, op1); } else { - dt.add (0, std::numeric_limits::max (), l, op1); + dt.add (0, std::numeric_limits::max (), single, op1); } // add this to the layers using the special join operator that @@ -603,6 +751,117 @@ LayerMap::insert (const LDPair &p1, const LDPair &p2, unsigned int l, const Laye } } +void +LayerMap::unmap (const LDPair &f) +{ + unmap (f, f); +} + +void +LayerMap::unmap (const std::string &name) +{ + m_name_map.erase (name); +} + +void +LayerMap::unmap (const LayerProperties &f) +{ + if (f.name.empty () || is_static_ld (f.layer) || is_static_ld (f.datatype)) { + unmap (db::LDPair (f.layer, f.datatype)); + } + if (! f.name.empty ()) { + unmap (f.name); + } +} + + + +void +LayerMap::unmap (const LDPair &p1, const LDPair &p2) +{ + if (m_ld_map.begin () == m_ld_map.end ()) { + return; + } + + LmapEraseDatatypeInterval op (p1.datatype, p2.datatype); + if (db::is_static_ld (p1.layer) && db::is_static_ld (p2.layer)) { + m_ld_map.add (p1.layer, p2.layer + 1, LayerMap::datatype_map (), op); + } else { + m_ld_map.add (m_ld_map.begin ()->first.first, m_ld_map.end ()->first.second, LayerMap::datatype_map (), op); + } +} + + +void +LayerMap::unmap_expr (const std::string &expr) +{ + tl::Extractor ex (expr.c_str ()); + unmap_expr (ex); + ex.expect_end (); +} + +void +LayerMap::unmap_expr (tl::Extractor &ex) +{ + try { + + bool round_bracket = false, square_bracket = false; + if (ex.test ("(")) { + round_bracket = true; + } else if (ex.test ("[")) { + square_bracket = true; + } + + do { + + tl::Extractor ex_saved = ex; + + std::string name; + ld_type n; + if (! ex.try_read (n) && ex.try_read_word_or_quoted (name)) { + + m_name_map.erase (name); + + } else { + + ex = ex_saved; + ld_interval_vector vl, vd; + + parse_intervals (ex, vl); + + if (ex.test ("/")) { + parse_intervals (ex, vd); + } else { + vd.push_back (ld_interval (0, 0)); + } + + for (ld_interval_vector::const_iterator li = vl.begin (); li != vl.end (); ++li) { + for (ld_interval_vector::const_iterator di = vd.begin (); di != vd.end (); ++di) { + unmap (LDPair (li->first, di->first), LDPair (li->second, di->second)); + } + } + + } + + } while (ex.test (";") || ex.test (",")); + + if (ex.test (":")) { + // ignore target layers + LayerProperties lp; + lp.read (ex, true); + } + + if (round_bracket) { + ex.expect (")"); + } else if (square_bracket) { + ex.expect ("]"); + } + + } catch (...) { + throw LayerSpecFormatException (ex.skip ()); + } +} + void LayerMap::clear () { @@ -646,6 +905,26 @@ LayerMap::to_string_file_format () const return os.str (); } +void +LayerMap::add_expr (const std::string &expr, unsigned int l) +{ + tl::Extractor ex (expr.c_str ()); + add_expr (ex, l); + ex.expect_end (); +} + +void +LayerMap::add_expr (tl::Extractor &ex, unsigned int l) +{ + if (ex.test ("+")) { + mmap_expr (ex, l); + } else if (ex.test ("-")) { + unmap_expr (ex); + } else { + map_expr (ex, l); + } +} + db::LayerMap LayerMap::from_string_file_format (const std::string &s) { @@ -668,7 +947,7 @@ LayerMap::from_string_file_format (const std::string &s) } else { if (! ex.at_end ()) { - lm.map_expr (ex, l); + lm.add_expr (ex, l); if (ex.test ("#") || ex.test ("//")) { // ignore comments } else { @@ -703,7 +982,7 @@ namespace tl while (! ex.test (")") && ! ex.at_end ()) { std::string m; ex.read_word_or_quoted (m); - t.map_expr (m, l); + t.add_expr (m, l); ++l; ex.test (";"); } @@ -723,7 +1002,7 @@ namespace tl while (! ex.test (")") && ! ex.at_end ()) { std::string m; ex.read_word_or_quoted (m); - t.map_expr (m, l); + t.add_expr (m, l); ++l; ex.test (";"); } diff --git a/src/db/db/dbStreamLayers.h b/src/db/db/dbStreamLayers.h index b142d7d95..61eaad77a 100644 --- a/src/db/db/dbStreamLayers.h +++ b/src/db/db/dbStreamLayers.h @@ -35,6 +35,7 @@ #include #include +#include namespace db { @@ -161,6 +162,15 @@ struct DB_PUBLIC LDPair * and effectively rename a layer or add layer name information to * a GDS layer/datatype layer. * + * "Unmapping" can be used to create "holes" in ranges of layers. + * For example, by first mapping layers 1 to 100, datatype 0 and then + * unmapping layer 50, datatype 0, the layers 1 to 49 and 51 to 100, datatype 0 + * are mapped. + * + * The layer map supports multi-mapping. That is, one input layer is + * mapped to multiple target layers. It also supports merging but + * mapping different input layers to a single target layer. + * * A layer map object can be used as a standalone object or in * conjunction with a layout object. As a standalone object, the * logical layers (indexes) are simply consecutive numbers. @@ -178,24 +188,52 @@ class DB_PUBLIC LayerMap : public gsi::ObjectBase { public: - typedef tl::interval_map datatype_map; + typedef tl::interval_map > datatype_map; typedef tl::interval_map ld_map; typedef ld_map::const_iterator const_iterator_layers; typedef datatype_map::const_iterator const_iterator_datatypes; - typedef std::map::const_iterator const_iterator_names; + typedef std::map >::const_iterator const_iterator_names; /** * @brief The constructor for an empty map */ LayerMap (); - /** + /** + * @brief Returns the first logical layer for a given layer specification + */ + template + std::pair first_logical (const L &p) const + { + std::set r = logical (p); + if (r.empty ()) { + return std::make_pair (false, 0); + } else { + return std::make_pair (true, *r.begin ()); + } + } + + /** + * @brief Returns the first logical layer for a given layer specification + */ + template + std::pair first_logical (const L &p, db::Layout &layout) const + { + std::set r = logical (p, layout); + if (r.empty ()) { + return std::make_pair (false, 0); + } else { + return std::make_pair (true, *r.begin ()); + } + } + + /** * @brief Query a layer mapping * * @return A pair telling if the layer is mapped (first=true) and * the logical layer mapped (second) if this is the case. */ - std::pair logical (const LDPair &p) const; + std::set logical (const LDPair &p) const; /** * @brief Query a layer mapping from a name @@ -203,7 +241,7 @@ public: * @return A pair telling if the layer is mapped (first=true) and * the logical layer mapped (second) if this is the case. */ - std::pair logical (const std::string &name) const; + std::set logical (const std::string &name) const; /** * @brief Query a layer mapping from a name or LDPair @@ -213,7 +251,7 @@ public: * * @param p The layer that is looked for */ - std::pair logical (const db::LayerProperties &p) const; + std::set logical (const db::LayerProperties &p) const; /** * @brief Query or install a layer mapping from a name or LDPair @@ -227,14 +265,29 @@ public: * the logical layers are placeholder values which will be replaced by * true layers during this method if a new layer is requested. */ - std::pair logical (const db::LayerProperties &p, db::Layout &layout) const; + std::set logical (const db::LayerProperties &p, db::Layout &layout) const; /** * @brief Query or install a layer mapping from a LDPair * * See the version for LayerProperties about details. */ - std::pair logical (const db::LDPair &p, db::Layout &layout) const; + std::set logical (const db::LDPair &p, db::Layout &layout) const; + + /** + * @brief Returns a value indicating whether a layer (given by layer/datatype) is mapped + */ + bool is_mapped (const LDPair &p) const; + + /** + * @brief Returns a value indicating whether the given named layer is mapped + */ + bool is_mapped (const std::string &name) const; + + /** + * @brief Returns a value indicating whether a layer is mapped + */ + bool is_mapped (const db::LayerProperties &p) const; /** * @brief Gets the target layer for a given logical layer @@ -270,17 +323,81 @@ public: std::vector get_layers () const; /** - * @brief Map a ldpair to a logical layer + * @brief Single-map a physical to a logical layer + * + * "Single-mapping" substitutes a layer mapping. "Multimapping" (mmap_..) + * adds to a given mapping and allows generating 1:n mappings (m:n in fact). */ - void map (const LDPair &p, unsigned int l); + template + void map (const S &p, unsigned int l) + { + unmap (p); + mmap (p, l); + } /** - * @brief Map a name to a logical layer + * @brief Single-map a physical to a logical layer with a target layer */ - void map (const std::string &name, unsigned int l); + template + void map (const S &p, unsigned int l, const LayerProperties &t) + { + unmap (p); + mmap (p, l, t); + } /** - * @brief Map a name or LDPair to a logical layer + * @brief Single-map a physical layer interval with a target layer + */ + void map (const LDPair &p1, const LDPair &p2, unsigned int l) + { + unmap (p1, p2); + mmap (p1, p2, l); + } + + /** + * @brief Single-map a physical layer interval with a target layer + */ + void map (const LDPair &p1, const LDPair &p2, unsigned int l, const LayerProperties &t) + { + unmap (p1, p2); + mmap (p1, p2, l, t); + } + + /** + * @brief Single-map a physical layer interval (given by an expression) + */ + void map_expr (const std::string &expr, unsigned int l) + { + unmap_expr (expr); + mmap_expr (expr, l); + } + + /** + * @brief Same a map_expr with a string argument but taking the expression for a tl::Extractor + */ + void map_expr (tl::Extractor &ex, unsigned int l) + { + tl::Extractor ex1 = ex; + unmap_expr (ex1); + mmap_expr (ex, l); + } + + /** + * @brief Multi-map a ldpair to a logical layer + * + * "Multimapping" will not substitute but add to the mapping. + */ + void mmap (const LDPair &p, unsigned int l); + + /** + * @brief Multi-map a name to a logical layer + * + * "Multimapping" will not substitute but add to the mapping. + */ + void mmap (const std::string &name, unsigned int l); + + /** + * @brief Multi-map a name or LDPair to a logical layer * * The algorithm chooses the LDPair from the LayerProperties structure and/or * the name if no LDPair is given. If the source LayerProperties structure does @@ -289,26 +406,26 @@ public: * @param f The source (where to derive the match expression from) * @param l The logical layer to map to the match expression */ - void map (const LayerProperties &f, unsigned int l); + void mmap (const LayerProperties &f, unsigned int l); /** - * @brief Map a ldpair to a logical layer with a target layer + * @brief Multi-map a ldpair to a logical layer with a target layer * * The target layer specifies which layer to create for the * corresponding input. */ - void map (const LDPair &p, unsigned int l, const LayerProperties &t); + void mmap (const LDPair &p, unsigned int l, const LayerProperties &t); /** - * @brief Map a name to a logical layer with a target layer + * @brief Multi-map a name to a logical layer with a target layer * * The target layer specifies which layer to create for the * corresponding input. */ - void map (const std::string &name, unsigned int l, const LayerProperties &t); + void mmap (const std::string &name, unsigned int l, const LayerProperties &t); /** - * @brief Map a name or LDPair to a logical layer with a target layer + * @brief Multi-map a name or LDPair to a logical layer with a target layer * * The algorithm chooses the LDPair from the LayerProperties structure or * the name if no LDPair is given. If the source LayerProperties structure does @@ -318,23 +435,23 @@ public: * @param l The logical layer to map to the match expression * @param t The target layer to use for the mapped layer */ - void map (const LayerProperties &f, unsigned int l, const LayerProperties &t); + void mmap (const LayerProperties &f, unsigned int l, const LayerProperties &t); /** - * @brief Map a range of ldpair's to a logical layer + * @brief Multi-map a range of ldpair's to a logical layer * * The range is given by two pairs p1,p2. The layers * mapped are [p1.l,p2.l], the datatypes mapped are [p1.d,p2.d]. */ - void map (const LDPair &p1, const LDPair &p2, unsigned int l); + void mmap (const LDPair &p1, const LDPair &p2, unsigned int l); /** - * @brief Map a range of ldpair's to a logical layer with a target layer + * @brief Multi-map a range of ldpair's to a logical layer with a target layer * * The range is given by two pairs p1,p2. The layers * mapped are [p1.l,p2.l], the datatypes mapped are [p1.d,p2.d]. */ - void map (const LDPair &p1, const LDPair &p2, unsigned int l, const LayerProperties &t); + void mmap (const LDPair &p1, const LDPair &p2, unsigned int l, const LayerProperties &t); /** * @brief Map a range given by a string expression to a logical layer @@ -364,12 +481,61 @@ public: * This method will throw a LayerSpecFormatException if * something is wrong with the format string */ - void map_expr (const std::string &expr, unsigned int l); + void mmap_expr (const std::string &expr, unsigned int l); /** * @brief Same a map_expr with a string argument but taking the expression for a tl::Extractor */ - void map_expr (tl::Extractor &ex, unsigned int l); + void mmap_expr (tl::Extractor &ex, unsigned int l); + + /** + * @brief Unmaps a LDPair + */ + void unmap (const LDPair &f); + + /** + * @brief Unmaps the layer with the given name + */ + void unmap (const std::string &name); + + /** + * @brief Unmaps a layer with the given layer properties + */ + void unmap (const LayerProperties &f); + + /** + * @brief Removes any mapping for a range of ldpair's + * + * The range is given by two pairs p1,p2. The layers + * between [p1.l,p2.l] and with datatypes between [p1.d,p2.d] are unmapped. + */ + void unmap (const LDPair &p1, const LDPair &p2); + + /** + * @brief Removes any mapping for the layers given by the expression + */ + void unmap_expr (const std::string &expr); + + /** + * @brief Removes any mapping for the layers given by the expression + */ + void unmap_expr (tl::Extractor &ex); + + /** + * @brief Generic expression mapping + * + * This generic mapping function takes a mapping expression. If it starts with "+", + * "mmap" is used, if it starts with "-", "unmap" is used. Otherwise, "map" is used. + */ + void add_expr (const std::string &expr, unsigned int l); + + /** + * @brief Generic expression mapping + * + * This generic mapping function takes a mapping expression. If it starts with "+", + * "mmap" is used, if it starts with "-", "unmap" is used. Otherwise, "map" is used. + */ + void add_expr (tl::Extractor &ex, unsigned int l); /** * @brief Prepares a layer mapping object for reading @@ -454,7 +620,7 @@ public: private: ld_map m_ld_map; - std::map m_name_map; + std::map > m_name_map; std::map m_target_layers; std::vector m_placeholders; unsigned int m_next_index; @@ -462,12 +628,12 @@ private: void insert (const LDPair &p1, const LDPair &p2, unsigned int l, const LayerProperties *t); void insert (const std::string &name, unsigned int l, const LayerProperties *t); - std::pair logical_internal (const LDPair &p, bool allow_placeholder) const; - std::pair logical_internal (const std::string &name, bool allow_placeholder) const; - std::pair logical_internal (const db::LayerProperties &p, bool allow_placeholder) const; + std::set logical_internal (const LDPair &p, bool allow_placeholder) const; + std::set logical_internal (const std::string &name, bool allow_placeholder) const; + std::set logical_internal (const db::LayerProperties &p, bool allow_placeholder) const; - std::pair substitute_placeholder (const db::LayerProperties &p, unsigned int ph, db::Layout &layout); - bool is_placeholder (unsigned int l) const; + std::set substitute_placeholder (const db::LayerProperties &p, const std::set &ph, db::Layout &layout); + bool is_placeholder (const std::set &l) const; }; } diff --git a/src/db/db/gsiDeclDbCommonStreamOptions.cc b/src/db/db/gsiDeclDbCommonStreamOptions.cc index 32b8ede8c..31245a050 100644 --- a/src/db/db/gsiDeclDbCommonStreamOptions.cc +++ b/src/db/db/gsiDeclDbCommonStreamOptions.cc @@ -85,12 +85,12 @@ static void set_properties_enabled (db::LoadLayoutOptions *options, bool l) options->get_options ().enable_properties = l; } -static db::CommonReader::CellConflictResolution get_cell_conflict_resolution (const db::LoadLayoutOptions *options) +static db::CellConflictResolution get_cell_conflict_resolution (const db::LoadLayoutOptions *options) { return options->get_options ().cell_conflict_resolution; } -static void set_cell_conflict_resolution (db::LoadLayoutOptions *options, db::CommonReader::CellConflictResolution cc) +static void set_cell_conflict_resolution (db::LoadLayoutOptions *options, db::CellConflictResolution cc) { options->get_options ().cell_conflict_resolution = cc; } @@ -188,18 +188,18 @@ gsi::ClassExt common_reader_options ( ); -gsi::EnumIn decl_dbCommonReader_CellConflictResolution ("db", "CellConflictResolution", - gsi::enum_const ("AddToCell", db::CommonReader::AddToCell, +gsi::EnumIn decl_dbCommonReader_CellConflictResolution ("db", "CellConflictResolution", + gsi::enum_const ("AddToCell", db::AddToCell, "@brief Add content to existing cell\n" "This is the mode use in before version 0.27. Content of new cells is simply added to existing cells with the same name." ) + - gsi::enum_const ("OverwriteCell", db::CommonReader::OverwriteCell, + gsi::enum_const ("OverwriteCell", db::OverwriteCell, "@brief The old cell is overwritten entirely (including child cells which are not used otherwise)\n" ) + - gsi::enum_const ("SkipNewCell", db::CommonReader::SkipNewCell, + gsi::enum_const ("SkipNewCell", db::SkipNewCell, "@brief The new cell is skipped entirely (including child cells which are not used otherwise)\n" ) + - gsi::enum_const ("RenameCell", db::CommonReader::RenameCell, + gsi::enum_const ("RenameCell", db::RenameCell, "@brief The new cell will be renamed to become unique\n" ), "@brief This enum specifies how cell conflicts are handled if a layout read into another layout and a cell name conflict arises. " diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 2e85264b4..e9539343d 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -39,6 +39,7 @@ #include "dbLayoutUtils.h" #include "dbLayerMapping.h" #include "dbCellMapping.h" +#include "dbTechnology.h" #include "tlStream.h" namespace gsi @@ -750,7 +751,7 @@ static db::Cell *create_cell2 (db::Layout *layout, const std::string &name, cons static db::Cell *create_cell3 (db::Layout *layout, const std::string &name, const std::string &libname) { - db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (libname); + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (libname, layout->technology_name ()); if (! lib) { return 0; } @@ -765,7 +766,7 @@ static db::Cell *create_cell3 (db::Layout *layout, const std::string &name, cons static db::Cell *create_cell4 (db::Layout *layout, const std::string &name, const std::string &libname, const std::map ¶ms) { - db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (libname); + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (libname, layout->technology_name ()); if (! lib) { return 0; } @@ -988,6 +989,21 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.25." ) + + gsi::method ("technology_name", &db::Layout::technology_name, + "@brief Gets the name of the technology this layout is associated with\n" + "This method has been introduced in version 0.27. Before that, the technology has been kept in the 'technology' meta data element." + ) + + gsi::method ("technology", &db::Layout::technology, + "@brief Gets the \\Technology object of the technology this layout is associated with or nil if the layout is not associated with a technology\n" + "This method has been introduced in version 0.27. Before that, the technology has been kept in the 'technology' meta data element." + ) + + gsi::method ("technology_name=", &db::Layout::set_technology_name, gsi::arg ("name"), + "@brief Sets the name of the technology this layout is associated with\n" + "Changing the technology name will re-assess all library references because libraries can be technology specified. " + "Cell layouts may be substituted during this re-assessment.\n" + "\n" + "This method has been introduced in version 0.27." + ) + gsi::method ("is_editable?", &db::Layout::is_editable, "@brief Returns a value indicating whether the layout is editable.\n" "@return True, if the layout is editable.\n" diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index 0476a5087..a156b6375 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -43,9 +43,14 @@ static db::Library *new_lib () return new db::Library (); } -static db::Library *library_by_name (const std::string &name) +static db::Library *library_by_name (const std::string &name, const std::string &for_technology) { - return db::LibraryManager::instance ().lib_ptr_by_name (name); + return db::LibraryManager::instance ().lib_ptr_by_name (name, for_technology); +} + +static db::Library *library_by_id (db::lib_id_type id) +{ + return db::LibraryManager::instance ().lib (id); } static std::vector library_names () @@ -57,6 +62,15 @@ static std::vector library_names () return r; } +static std::vector library_ids () +{ + std::vector r; + for (db::LibraryManager::iterator l = db::LibraryManager::instance ().begin (); l != db::LibraryManager::instance ().end (); ++l) { + r.push_back (l->second); + } + return r; +} + static void register_lib (db::Library *lib, const std::string &name) { lib->set_name (name); @@ -110,22 +124,45 @@ LibraryClass decl_Library ("db", "Library", gsi::constructor ("new", &new_lib, "@brief Creates a new, empty library" ) + - gsi::method ("library_by_name", &library_by_name, gsi::arg ("name"), + gsi::method ("library_by_name", &library_by_name, gsi::arg ("name"), gsi::arg ("for_technology", std::string (), "unspecific"), "@brief Gets a library by name\n" "Returns the library object for the given name. If the name is not a valid\n" "library name, nil is returned.\n" + "\n" + "Different libraries can be registered under the same names for different technologies. When a technology name is given in 'for_technologies', " + "the first library matching this technology is returned. If no technology is given, the first library is returned.\n" + "\n" + "The technology selector has been introduced in version 0.27." + ) + + gsi::method ("library_by_id", &library_by_id, gsi::arg ("id"), + "@brief Gets the library object for the given ID\n" + "If the ID is not valid, nil is returned.\n" + "\n" + "This method has been introduced in version 0.27." ) + gsi::method ("library_names", &library_names, "@brief Returns a list of the names of all libraries registered in the system.\n" + "\n" + "NOTE: starting with version 0.27, the name of a library does not need to be unique if libraries are associated with specific technologies. " + "This method will only return the names and it's not possible not unambiguously derive the library object. It is recommended to use " + "\\library_ids and \\library_by_id to obtain the library unambiguously." + ) + + gsi::method ("library_ids", &library_ids, + "@brief Returns a list of valid library IDs.\n" + "See \\library_names for the reasoning behind this method." + "\n" + "This method has been introduced in version 0.27." ) + gsi::method_ext ("register", ®ister_lib, gsi::arg ("name"), "@brief Registers the library with the given name\n" "\n" "This method can be called in the constructor to register the library after \n" "the layout object has been filled with content. If a library with that name\n" - "already exists, it will be replaced with this library. \n" + "already exists for the same technologies, it will be replaced with this library. \n" "\n" "This method will set the libraries' name.\n" + "\n" + "The technology specific bahvior has been introduced in version 0.27." ) + gsi::method_ext ("delete", &delete_lib, "@brief Deletes the library\n" diff --git a/src/db/db/gsiDeclDbReader.cc b/src/db/db/gsiDeclDbReader.cc index 24b4e5b2e..19b82c6d3 100644 --- a/src/db/db/gsiDeclDbReader.cc +++ b/src/db/db/gsiDeclDbReader.cc @@ -36,17 +36,23 @@ namespace gsi static bool lm_is_mapped (const db::LayerMap *layer_map, const db::LayerProperties &lp) { - return layer_map->logical (lp).first; + return layer_map->is_mapped (lp); } static int lm_logical (const db::LayerMap *layer_map, const db::LayerProperties &lp) { - std::pair lm = layer_map->logical (lp); - return lm.first ? int (lm.second) : -1; + std::set lm = layer_map->logical (lp); + return lm.empty () ? -1 : int (*lm.begin ()); } - static db::LayerProperties + static std::set + lm_logicals (const db::LayerMap *layer_map, const db::LayerProperties &lp) + { + return layer_map->logical (lp); + } + + static db::LayerProperties lm_mapping (const db::LayerMap *layer_map, unsigned int l) { return layer_map->mapping (l); @@ -82,16 +88,74 @@ namespace gsi layer_map->map_expr (s, l); } + static void + lm_mmap (db::LayerMap *layer_map, const db::LayerProperties &lp, unsigned int l) + { + layer_map->mmap (lp, l); + } + + static void + lm_mmap_with_target (db::LayerMap *layer_map, const db::LayerProperties &lp, unsigned int l, const db::LayerProperties &t) + { + layer_map->mmap (lp, l, t); + } + + static void + lm_mmap_interval (db::LayerMap *layer_map, const db::LayerProperties &lp1, const db::LayerProperties &lp2, unsigned int l) + { + layer_map->mmap (ldpair_from_lp (lp1), ldpair_from_lp (lp2), l); + } + + static void + lm_mmap_interval_with_target (db::LayerMap *layer_map, const db::LayerProperties &lp1, const db::LayerProperties &lp2, unsigned int l, const db::LayerProperties &t) + { + layer_map->mmap (ldpair_from_lp (lp1), ldpair_from_lp (lp2), l, t); + } + + static void + lm_mmap_string (db::LayerMap *layer_map, std::string &s, unsigned int l) + { + layer_map->mmap_expr (s, l); + } + + static void + lm_unmap (db::LayerMap *layer_map, const db::LayerProperties &lp) + { + layer_map->unmap (lp); + } + + static void + lm_unmap_interval (db::LayerMap *layer_map, const db::LayerProperties &lp1, const db::LayerProperties &lp2) + { + layer_map->unmap (ldpair_from_lp (lp1), ldpair_from_lp (lp2)); + } + + static void + lm_unmap_string (db::LayerMap *layer_map, std::string &s) + { + layer_map->unmap_expr (s); + } + Class decl_LayerMap ("db", "LayerMap", gsi::method_ext ("is_mapped?", &lm_is_mapped, gsi::arg ("layer"), "@brief Check, if a given physical layer is mapped\n" "@param layer The physical layer specified with an \\LayerInfo object.\n" "@return True, if the layer is mapped." ) + - gsi::method_ext ("logical", &lm_logical, gsi::arg ("layer"), + gsi::method_ext ("#logical", &lm_logical, gsi::arg ("layer"), "@brief Returns the logical layer (the layer index in the layout object) for a given physical layer.n" "@param layer The physical layer specified with an \\LayerInfo object.\n" "@return The logical layer index or -1 if the layer is not mapped." + "\n" + "This method is deprecated with version 0.27 as in this version, layers can be mapped to multiple targets which " + "this method can't capture. Use \\logicals instead.\n" + ) + + gsi::method_ext ("logicals", &lm_logicals, gsi::arg ("layer"), + "@brief Returns the logical layers for a given physical layer.n" + "@param layer The physical layer specified with an \\LayerInfo object.\n" + "@return This list of logical layers this physical layer as mapped to or empty if there is no mapping." + "\n" + "This method has been introduced in version 0.27.\n" ) + gsi::method ("mapping_str", &db::LayerMap::mapping_str, gsi::arg ("log_layer"), "@brief Returns the mapping string for a given logical layer\n" @@ -162,11 +226,84 @@ namespace gsi "separated by a hyphen. Examples are: \"1/2\", \"1-5/0\", \"1,2,5/0\",\n" "\"1/5;5/6\".\n" "\n" - "A target layer can be specified with the \":\" notation where " - "the target is a valid layer specification string (i.e. \"1/0\").\n" + "layer/datatype wildcards can be specified with \"*\". When \"*\" is used\n" + "for the upper limit, it is equivalent to \"all layer above\". When used\n" + "alone, it is equivalent to \"all layers\". Examples: \"1 / *\", \"* / 10-*\"\n" + "\n" + "Named layers are specified simply by specifying the name, if\n" + "necessary in single or double quotes (if the name begins with a digit or\n" + "contains non-word characters). layer/datatype and name descriptions can\n" + "be mixed, i.e. \"AA;1/5\" (meaning: name \"AA\" or layer 1/datatype 5).\n" + "\n" + "A target layer can be specified with the \":\" notation, where\n" + "target is a valid string for a LayerProperties() object.\n" + "\n" + "A target can include relative layer/datatype specifications and wildcards.\n" + "For example, \"1-10/0: *+1/0\" will add 1 to the original layer number.\n" + "\"1-10/0-50: * / *\" will use the original layers.\n" "\n" "Target mapping has been added in version 0.20.\n" ) + + gsi::method_ext ("mmap", &lm_mmap, gsi::arg ("phys_layer"), gsi::arg ("log_layer"), + "@brief Maps a physical layer to a logical one and adds to existing mappings\n" + "\n" + "This method acts like the corresponding 'map' method, but adds the logical layer to the receivers of the " + "given physical one. Hence this method implements 1:n mapping capabilities.\n" + "For backward compatibility, 'map' still substitutes mapping.\n" + "\n" + "Multi-mapping has been added in version 0.27.\n" + ) + + gsi::method_ext ("mmap", &lm_mmap_with_target, gsi::arg ("phys_layer"), gsi::arg ("log_layer"), gsi::arg ("target_layer"), + "@brief Maps a physical layer to a logical one, adds to existing mappings and specifies a target layer\n" + "\n" + "This method acts like the corresponding 'map' method, but adds the logical layer to the receivers of the " + "given physical one. Hence this method implements 1:n mapping capabilities.\n" + "For backward compatibility, 'map' still substitutes mapping.\n" + "\n" + "Multi-mapping has been added in version 0.27.\n" + ) + + gsi::method_ext ("mmap", &lm_mmap_interval, gsi::arg ("pl_start"), gsi::arg ("pl_stop"), gsi::arg ("log_layer"), + "@brief Maps a physical layer from the given interval to a logical one and adds to existing mappings\n" + "\n" + "This method acts like the corresponding 'map' method, but adds the logical layer to the receivers of the " + "given physical one. Hence this method implements 1:n mapping capabilities.\n" + "For backward compatibility, 'map' still substitutes mapping.\n" + "\n" + "Multi-mapping has been added in version 0.27.\n" + ) + + gsi::method_ext ("mmap", &lm_mmap_interval_with_target, gsi::arg ("pl_start"), gsi::arg ("pl_stop"), gsi::arg ("log_layer"), gsi::arg ("layer_properties"), + "@brief Maps a physical layer from the given interval to a logical one, adds to existing mappings and specifies a target layer\n" + "\n" + "This method acts like the corresponding 'map' method, but adds the logical layer to the receivers of the " + "given physical one. Hence this method implements 1:n mapping capabilities.\n" + "For backward compatibility, 'map' still substitutes mapping.\n" + "\n" + "Multi-mapping has been added in version 0.27.\n" + ) + + gsi::method_ext ("mmap", &lm_mmap_string, gsi::arg ("map_expr"), gsi::arg ("log_layer"), + "@brief Maps a physical layer given by an expression to a logical one and adds to existing mappings\n" + "\n" + "This method acts like the corresponding 'map' method, but adds the logical layer to the receivers of the " + "given physical one. Hence this method implements 1:n mapping capabilities.\n" + "For backward compatibility, 'map' still substitutes mapping.\n" + "\n" + "Multi-mapping has been added in version 0.27.\n" + ) + + gsi::method_ext ("unmap", &lm_unmap, gsi::arg ("phys_layer"), + "@brief Unmaps the given layer\n" + "Unmapping will remove the specific layer from the mapping. This method allows generating " + "'mapping holes' by first mapping a range and then unmapping parts of it.\n" + "\n" + "This method has been introduced in version 0.27." + ) + + gsi::method_ext ("unmap", &lm_unmap_interval, gsi::arg ("pl_start"), gsi::arg ("pl_stop"), + "@brief Unmaps the layers from the given interval\n" + "This method has been introduced in version 0.27." + ) + + gsi::method_ext ("unmap", &lm_unmap_string, gsi::arg ("expr"), + "@brief Unmaps the layers from the given expression\n" + "This method has been introduced in version 0.27." + ) + gsi::method ("clear", &db::LayerMap::clear, "@brief Clears the map\n" ) + @@ -221,7 +358,28 @@ namespace gsi "ly.read(\"input.gds\", lo)\n" "@/code\n" "\n" - "The LayerMap class has been introduced in version 0.18." + "1:n mapping is supported: a physical layer can be mapped to multiple logical layers using 'mmap' instead of 'map'. When using this variant, " + "mapping acts additive.\n" + "The following example will map layer 1, datatypes 0 to 255 to logical layer 0, and layer 1, datatype 17 to logical layers " + "0 plus 1:" + "\n" + "@code" + "lm = RBA::LayerMap::new\n" + "lm.map(\"1/0-255\", 0) # (can be 'mmap' too)\n" + "lm.mmap(\"1/17\", 1)\n" + "@/code\n" + "\n" + "'unmapping' allows removing a mapping. This allows creating 'holes' in mapping ranges. The followin example maps " + "layer 1, datatypes 0 to 16 and 18 to 255 to logical layer 0:" + "\n" + "@code" + "lm = RBA::LayerMap::new\n" + "lm.map(\"1/0-255\", 0)\n" + "lm.unmap(\"1/17\")\n" + "@/code\n" + "\n" + "The LayerMap class has been introduced in version 0.18. Target layer have been introduced in version 0.20. " + "1:n mapping and unmapping has been introduced in version 0.27." ); // NOTE: the contribution comes from format specific extensions. diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index addce4eff..bbdfe3e4d 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -20,9 +20,12 @@ */ - - #include "dbLayout.h" +#include "dbLibraryManager.h" +#include "dbLibrary.h" +#include "dbColdProxy.h" +#include "dbLibraryProxy.h" +#include "dbTextWriter.h" #include "tlString.h" #include "tlUnitTest.h" @@ -464,3 +467,176 @@ TEST(4) prop_id = g.properties_repository ().properties_id (ps); EXPECT_EQ (el.property_ids_dirty, true); } + +static std::string l2s (const db::Layout &layout) +{ + tl::OutputStringStream os; + tl::OutputStream ostream (os); + db::TextWriter writer (ostream); + writer.write (layout); + return os.string (); +} + +TEST(5) +{ + // Technology management and library substitution + + db::cell_index_type ci; + unsigned int li; + db::Cell *cell; + + db::Library *lib_a = new db::Library (); + lib_a->set_name ("LIB"); + ci = lib_a->layout ().add_cell ("LIBCELL"); + li = lib_a->layout ().insert_layer (db::LayerProperties (1, 0)); + lib_a->layout ().cell (ci).shapes (li).insert (db::Box (0, 0, 100, 200)); + lib_a->add_technology ("A"); + db::LibraryManager::instance ().register_lib (lib_a); + + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "A").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "A").second, lib_a->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_ptr_by_name ("LIB", "A") == lib_a, true); + + db::Library *lib_b = new db::Library (); + lib_b->set_name ("LIB"); + ci = lib_b->layout ().add_cell ("LIBCELL"); + li = lib_b->layout ().insert_layer (db::LayerProperties (2, 0)); + lib_b->layout ().cell (ci).shapes (li).insert (db::Box (0, 0, 200, 100)); + lib_b->add_technology ("B"); + db::LibraryManager::instance ().register_lib (lib_b); + + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "B").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "B").second, lib_b->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_ptr_by_name ("LIB", "B") == lib_b, true); + + db::Library *lib_c = new db::Library (); + lib_c->set_name ("LIB"); + ci = lib_c->layout ().add_cell ("LIBCELL2"); + li = lib_c->layout ().insert_layer (db::LayerProperties (2, 0)); + lib_c->layout ().cell (ci).shapes (li).insert (db::Box (0, 0, 200, 100)); + lib_c->add_technology ("C"); + db::LibraryManager::instance ().register_lib (lib_c); + + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "C").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("LIB", "C").second, lib_c->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_ptr_by_name ("LIB", "C") == lib_c, true); + + db::Manager m; + db::Layout l (&m); + EXPECT_EQ (l.technology_name (), ""); + + db::ProxyContextInfo info; + info.lib_name = "LIB"; + info.cell_name = "LIBCELL"; + + cell = l.recover_proxy (info); + EXPECT_EQ (dynamic_cast (cell) != 0, true); + EXPECT_EQ (cell->get_qualified_name (), "LIB.LIBCELL"); + EXPECT_EQ (cell->get_basic_name (), "LIBCELL"); + EXPECT_EQ (cell->get_display_name (), "LIB.LIBCELL"); + + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nend_cell\nend_lib\n"); + + // now restore the proxies + l.set_technology_name ("A"); + EXPECT_EQ (l.technology_name (), "A"); + + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 1 0 {0 0} {100 200}\nend_cell\nend_lib\n"); + + // now switch to cold proxies again as the technology does not have "LIBCELL" (but rather LIBCELL2) + l.set_technology_name ("C"); + EXPECT_EQ (l.technology_name (), "C"); + + cell = &l.cell (l.cell_by_name ("LIBCELL").second); + EXPECT_EQ (dynamic_cast (cell) != 0, true); + EXPECT_EQ (cell->get_qualified_name (), "LIB.LIBCELL"); + EXPECT_EQ (cell->get_basic_name (), "LIBCELL"); + EXPECT_EQ (cell->get_display_name (), "LIB.LIBCELL"); + + // NOTE: the box on 1/0 retained + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 1 0 {0 0} {100 200}\nend_cell\nend_lib\n"); + + // switch to another LIBCELL, this time using layer 2/0 + m.transaction ("switch_to_b"); + l.set_technology_name ("B"); + m.commit (); + + EXPECT_EQ (l.technology_name (), "B"); + cell = &l.cell (l.cell_by_name ("LIBCELL").second); + EXPECT_EQ (dynamic_cast (cell) != 0, true); + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 2 0 {0 0} {200 100}\nend_cell\nend_lib\n"); + + if (l.is_editable ()) { + + m.undo (); + EXPECT_EQ (l.technology_name (), "C"); + + cell = &l.cell (l.cell_by_name ("LIBCELL").second); + EXPECT_EQ (dynamic_cast (cell) != 0, true); + EXPECT_EQ (cell->get_qualified_name (), "LIB.LIBCELL"); + EXPECT_EQ (cell->get_basic_name (), "LIBCELL"); + EXPECT_EQ (cell->get_display_name (), "LIB.LIBCELL"); + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 1 0 {0 0} {100 200}\nend_cell\nend_lib\n"); + + m.redo (); + + EXPECT_EQ (l.technology_name (), "B"); + cell = &l.cell (l.cell_by_name ("LIBCELL").second); + EXPECT_EQ (dynamic_cast (cell) != 0, true); + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {LIBCELL}\nbox 2 0 {0 0} {200 100}\nend_cell\nend_lib\n"); + + } + + db::LibraryManager::instance ().delete_lib (lib_a); + db::LibraryManager::instance ().delete_lib (lib_b); + db::LibraryManager::instance ().delete_lib (lib_c); +} + +TEST(6) +{ + // Cold proxies and context serialization + db::Cell *cell; + + db::Manager m; + db::Layout l (&m); + + EXPECT_EQ (l.technology_name (), ""); + + db::ProxyContextInfo info; + info.lib_name = "Basic"; + info.pcell_name = "CIRCLE"; + info.pcell_parameters ["actual_radius"] = tl::Variant (10.0); + info.pcell_parameters ["npoints"] = tl::Variant (8); + info.pcell_parameters ["layer"] = tl::Variant (db::LayerProperties (1, 0)); + + m.transaction ("import"); + cell = l.recover_proxy (info); + m.commit (); + EXPECT_EQ (cell->get_qualified_name (), "Basic.CIRCLE"); + EXPECT_EQ (cell->get_basic_name (), "CIRCLE"); + EXPECT_EQ (cell->get_display_name (), "Basic.CIRCLE(l=1/0,r=10,n=8)"); + + 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; + l.get_context_info (cell->cell_index (), info2); + info2.pcell_parameters ["actual_radius"] = tl::Variant (5.0); + + m.transaction ("modify"); + db::cell_index_type ci = cell->cell_index (); + l.recover_proxy_as (ci, info2); + m.commit (); + cell = &l.cell (ci); + EXPECT_EQ (cell->get_qualified_name (), "Basic.CIRCLE"); + EXPECT_EQ (cell->get_basic_name (), "CIRCLE"); + EXPECT_EQ (cell->get_display_name (), "Basic.CIRCLE(l=1/0,r=5,n=8)"); + + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-2071 -5000} {-5000 -2071} {-5000 2071} {-2071 5000} {2071 5000} {5000 2071} {5000 -2071} {2071 -5000} {-2071 -5000}\nend_cell\nend_lib\n"); + + if (l.is_editable ()) { + m.undo (); + 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"); + m.redo (); + EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-2071 -5000} {-5000 -2071} {-5000 2071} {-2071 5000} {2071 5000} {5000 2071} {5000 -2071} {2071 -5000} {-2071 -5000}\nend_cell\nend_lib\n"); + } +} diff --git a/src/db/unit_tests/dbLibrariesTests.cc b/src/db/unit_tests/dbLibrariesTests.cc index 1be0d58f6..b7730a7d0 100644 --- a/src/db/unit_tests/dbLibrariesTests.cc +++ b/src/db/unit_tests/dbLibrariesTests.cc @@ -617,3 +617,86 @@ TEST(3) } } +TEST(4) +{ + tl::weak_ptr lib_a1 (new LIBT_A ()); + lib_a1->add_technology ("X"); + + tl::weak_ptr lib_a2 (new LIBT_A ()); + lib_a2->add_technology ("Y"); + + tl::weak_ptr lib_a3 (new LIBT_A ()); + lib_a3->add_technology ("X"); + + tl::weak_ptr lib_a4 (new LIBT_A ()); + + try { + + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Z").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").first, false); + + db::LibraryManager::instance ().register_lib (lib_a1.get ()); + + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Z").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").second, lib_a1->get_id ()); + + db::LibraryManager::instance ().register_lib (lib_a2.get ()); + + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Z").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").second, lib_a1->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Y").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Y").second, lib_a2->get_id ()); + + db::LibraryManager::instance ().register_lib (lib_a3.get ()); + // lib_a3 replaces lib_a1 + EXPECT_EQ (lib_a1.get () == 0, true); + + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Z").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").second, lib_a3->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Y").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Y").second, lib_a2->get_id ()); + + db::LibraryManager::instance ().register_lib (lib_a4.get ()); + + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A").second, lib_a4->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Z").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Z").second, lib_a4->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "").second, lib_a4->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").second, lib_a3->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Y").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Y").second, lib_a2->get_id ()); + + // because we switch to editable mode in between we have to clear the repository explicitly. Otherwise it's being cleared + // on next entry of TEST which will cause a segmentation fault if editable mode is different then. + if (lib_a1.get ()) { db::LibraryManager::instance ().delete_lib (lib_a1.get ()); } + if (lib_a2.get ()) { db::LibraryManager::instance ().delete_lib (lib_a2.get ()); } + if (lib_a3.get ()) { db::LibraryManager::instance ().delete_lib (lib_a3.get ()); } + if (lib_a4.get ()) { db::LibraryManager::instance ().delete_lib (lib_a4.get ()); } + + } catch (...) { + + // because we switch to editable mode in between we have to clear the repository explicitly. Otherwise it's being cleared + // on next entry of TEST which will cause a segmentation fault if editable mode is different then. + if (lib_a1.get ()) { db::LibraryManager::instance ().delete_lib (lib_a1.get ()); } + if (lib_a2.get ()) { db::LibraryManager::instance ().delete_lib (lib_a2.get ()); } + if (lib_a3.get ()) { db::LibraryManager::instance ().delete_lib (lib_a3.get ()); } + if (lib_a4.get ()) { db::LibraryManager::instance ().delete_lib (lib_a4.get ()); } + throw; + + } +} + diff --git a/src/db/unit_tests/dbStreamLayerTests.cc b/src/db/unit_tests/dbStreamLayerTests.cc index b0ccf4f10..81d6ab21a 100644 --- a/src/db/unit_tests/dbStreamLayerTests.cc +++ b/src/db/unit_tests/dbStreamLayerTests.cc @@ -29,16 +29,16 @@ TEST(1) db::LayerMap lm; lm.map (db::LDPair (1, 5), 17); - EXPECT_EQ (lm.logical (db::LDPair (1, 6)).first, false); - EXPECT_EQ (lm.logical (db::LDPair (1, 5)).first, true); - EXPECT_EQ (lm.logical (db::LDPair (1, 5)).second, (unsigned int) 17); + EXPECT_EQ (lm.first_logical (db::LDPair (1, 6)).first, false); + EXPECT_EQ (lm.first_logical (db::LDPair (1, 5)).first, true); + EXPECT_EQ (lm.first_logical (db::LDPair (1, 5)).second, (unsigned int) 17); lm.map (db::LDPair (1, 0), db::LDPair (5,0), 18); - EXPECT_EQ (lm.logical (db::LDPair (2, 0)).first, true); - EXPECT_EQ (lm.logical (db::LDPair (2, 0)).second, (unsigned int) 18); - EXPECT_EQ (lm.logical (db::LDPair (0, 0)).first, false); + EXPECT_EQ (lm.first_logical (db::LDPair (2, 0)).first, true); + EXPECT_EQ (lm.first_logical (db::LDPair (2, 0)).second, (unsigned int) 18); + EXPECT_EQ (lm.first_logical (db::LDPair (0, 0)).first, false); - EXPECT_EQ (lm.mapping_str (18), "1/0;2-5/0"); + EXPECT_EQ (lm.mapping_str (18), "1-5/0"); EXPECT_EQ (lm.mapping_str (17), "1/5"); lm.map (db::LDPair (2, 2), 18); @@ -62,18 +62,37 @@ TEST(1) lm.map_expr ("XP;10/7-8 : XN", 13); EXPECT_EQ (lm.mapping_str (13), "10/7-8;XP : XN"); - EXPECT_EQ (lm.logical ("XP").second, (unsigned int) 13); - EXPECT_EQ (lm.logical ("XP").first, true); - EXPECT_EQ (lm.logical (db::LDPair(10, 6)).first, false); - EXPECT_EQ (lm.logical (db::LDPair(10, 7)).first, true); - EXPECT_EQ (lm.logical (db::LDPair(10, 7)).second, (unsigned int) 13); + EXPECT_EQ (lm.first_logical ("XP").second, (unsigned int) 13); + EXPECT_EQ (lm.first_logical ("XP").first, true); + EXPECT_EQ (lm.first_logical (db::LDPair(10, 6)).first, false); + EXPECT_EQ (lm.first_logical (db::LDPair(10, 7)).first, true); + EXPECT_EQ (lm.first_logical (db::LDPair(10, 7)).second, (unsigned int) 13); EXPECT_EQ (lm.mapping (13).to_string (), "XN (10/7)"); lm.clear (); - EXPECT_EQ (lm.logical (db::LDPair(10, 7)).first, false); + EXPECT_EQ (lm.first_logical (db::LDPair(10, 7)).first, false); lm.map_expr ("'XP';10/7-8 : XN", 13); EXPECT_EQ (lm.mapping_str (13), "10/7-8;XP : XN"); + + // brackets, "add_expr" + lm.clear (); + lm.add_expr ("[1-10/*]", 1); + EXPECT_EQ (lm.mapping_str (1), "1-10/* : */*"); + lm.add_expr ("-(5/*)", 0); + EXPECT_EQ (lm.mapping_str (1), "1-4/*;6-10/* : */*"); + + lm.clear (); + lm.add_expr ("[1/15]", 1); + lm.add_expr ("+(1/5:1001/5)", 1); + // NOTE: the target is taken from the second expression (the last one wins) + EXPECT_EQ (lm.mapping_str (1), "1/5,15 : 1001/5"); + + lm.clear (); + lm.add_expr ("+(1/5:1001/5)", 1); + lm.add_expr ("[1/15]", 1); + // NOTE: the target is taken from the second expression (the last one wins) + EXPECT_EQ (lm.mapping_str (1), "1/5,15 : */*"); } TEST(2) @@ -349,55 +368,55 @@ TEST(6) EXPECT_EQ (layers_to_string (ly), "1/0,3/10"); std::pair p; - p = lm.logical (db::LayerProperties (1, 0)); + p = lm.first_logical (db::LayerProperties (1, 0)); EXPECT_EQ (p.first, true); EXPECT_EQ (p.second, (unsigned int) 0); - p = lm.logical (db::LayerProperties (2, 0)); + p = lm.first_logical (db::LayerProperties (2, 0)); EXPECT_EQ (p.first, false); - p = lm.logical (db::LayerProperties (3, 0)); + p = lm.first_logical (db::LayerProperties (3, 0)); EXPECT_EQ (p.first, false); - p = lm.logical (db::LayerProperties (3, 10)); + p = lm.first_logical (db::LayerProperties (3, 10)); EXPECT_EQ (p.first, true); EXPECT_EQ (p.second, (unsigned int) 1); - p = lm.logical (db::LayerProperties (3, 99)); + p = lm.first_logical (db::LayerProperties (3, 99)); EXPECT_EQ (p.first, true); EXPECT_EQ (p.second, (unsigned int) 1); EXPECT_EQ (layers_to_string (ly), "1/0,3/10"); // this will create layer 2/0 in the layout - p = lm.logical (db::LayerProperties (2, 0), ly); + p = lm.first_logical (db::LayerProperties (2, 0), ly); EXPECT_EQ (p.first, true); EXPECT_EQ (p.second, (unsigned int) 2); EXPECT_EQ (layers_to_string (ly), "1/0,3/10,2/0"); - p = lm.logical (db::LayerProperties (2, 0)); + p = lm.first_logical (db::LayerProperties (2, 0)); EXPECT_EQ (p.first, true); EXPECT_EQ (p.second, (unsigned int) 2); - p = lm.logical (db::LayerProperties (2, 0), ly); + p = lm.first_logical (db::LayerProperties (2, 0), ly); EXPECT_EQ (p.first, true); EXPECT_EQ (p.second, (unsigned int) 2); EXPECT_EQ (layers_to_string (ly), "1/0,3/10,2/0"); // this will create layer 2/42 in the layout - p = lm.logical (db::LayerProperties (2, 42), ly); + p = lm.first_logical (db::LayerProperties (2, 42), ly); EXPECT_EQ (p.first, true); EXPECT_EQ (p.second, (unsigned int) 3); EXPECT_EQ (layers_to_string (ly), "1/0,3/10,2/0,2/42"); - p = lm.logical (db::LayerProperties (2, 42)); + p = lm.first_logical (db::LayerProperties (2, 42)); EXPECT_EQ (p.first, true); EXPECT_EQ (p.second, (unsigned int) 3); - p = lm.logical (db::LayerProperties (2, 42), ly); + p = lm.first_logical (db::LayerProperties (2, 42), ly); EXPECT_EQ (p.first, true); EXPECT_EQ (p.second, (unsigned int) 3); @@ -429,19 +448,132 @@ TEST(7) EXPECT_EQ (layers_to_string (ly), "85/0,185/0,,"); std::pair p; - p = lm.logical (db::LayerProperties (85, 0)); + p = lm.first_logical (db::LayerProperties (85, 0)); EXPECT_EQ (p.first, false); EXPECT_EQ (p.second, (unsigned int) 0); - p = lm.logical (db::LayerProperties (185, 0)); + p = lm.first_logical (db::LayerProperties (185, 0)); EXPECT_EQ (p.first, false); EXPECT_EQ (p.second, (unsigned int) 0); - p = lm.logical (db::LayerProperties (10000, 0)); + p = lm.first_logical (db::LayerProperties (10000, 0)); EXPECT_EQ (p.first, true); EXPECT_EQ (p.second, (unsigned int) 1); - p = lm.logical (db::LayerProperties (10001, 0)); + p = lm.first_logical (db::LayerProperties (10001, 0)); EXPECT_EQ (p.first, true); EXPECT_EQ (p.second, (unsigned int) 0); } + +static std::string set2string (const std::set &set) +{ + std::string s; + for (std::set::const_iterator i = set.begin (); i != set.end (); ++i) { + if (i != set.begin ()) { + s += ","; + } + s += tl::to_string (*i); + } + return s; +} + +// multi-mapping, unmapping +TEST(8) +{ + db::LayerMap lm; + + unsigned int n = 0; + + // refinement + // all + lm.mmap_expr ("*/*", n++); + EXPECT_EQ (lm.mapping_str (0), "*/*"); + EXPECT_EQ (lm.to_string (), + "layer_map('*/*')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + // some + lm.mmap_expr ("*/1-10", n++); + EXPECT_EQ (lm.to_string (), + "layer_map('+*/*';'+*/1-10')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + // others + lm.mmap_expr ("*/5,15", n++); + + EXPECT_EQ (lm.to_string (), + "layer_map('+*/*';'+*/1-10';'+*/5,15')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + EXPECT_EQ (set2string (lm.logical (db::LDPair (0, 1000))), "0"); + EXPECT_EQ (set2string (lm.logical (db::LDPair (1, 1000))), "0"); + EXPECT_EQ (set2string (lm.logical (db::LDPair (0, 5))), "0,1,2"); + EXPECT_EQ (set2string (lm.logical (db::LDPair (0, 15))), "0,2"); + EXPECT_EQ (set2string (lm.logical (db::LDPair (0, 10))), "0,1"); + + // NOTE: the leading "+" indicates that the listed layers may go somewhere else, so we can't plainly map them + EXPECT_EQ (lm.mapping_str (0), "+*/*"); + EXPECT_EQ (lm.mapping_str (1), "+*/1-10"); + EXPECT_EQ (lm.mapping_str (2), "+*/5,15"); + EXPECT_EQ (lm.mapping_str (3), ""); + + lm = db::LayerMap (); + n = 0; + + // refinement + // all + lm.mmap_expr ("*/*", n++); + EXPECT_EQ (lm.mapping_str (0), "*/*"); + EXPECT_EQ (lm.to_string (), + "layer_map('*/*')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + // some + lm.mmap_expr ("1-10/*", n++); + EXPECT_EQ (lm.to_string (), + "layer_map('+*/*';'+1-10/*')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + // others + lm.mmap_expr ("5,15/*", n++); + + EXPECT_EQ (lm.to_string (), + "layer_map('+*/*';'+1-10/*';'+5/*;15/*')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + EXPECT_EQ (set2string (lm.logical (db::LDPair (1000, 0))), "0"); + EXPECT_EQ (set2string (lm.logical (db::LDPair (1000, 1))), "0"); + EXPECT_EQ (set2string (lm.logical (db::LDPair (5, 0))), "0,1,2"); + EXPECT_EQ (set2string (lm.logical (db::LDPair (15, 0))), "0,2"); + EXPECT_EQ (set2string (lm.logical (db::LDPair (10, 0))), "0,1"); + + // NOTE: the leading "+" indicates that the listed layers may go somewhere else, so we can't plainly map them + EXPECT_EQ (lm.mapping_str (0), "+*/*"); + EXPECT_EQ (lm.mapping_str (1), "+1-10/*"); + EXPECT_EQ (lm.mapping_str (2), "+5/*;15/*"); + EXPECT_EQ (lm.mapping_str (3), ""); + + lm = db::LayerMap (); + n = 0; + + lm.mmap_expr ("*/*", n++); + EXPECT_EQ (lm.mapping_str (0), "*/*"); + EXPECT_EQ (lm.to_string (), + "layer_map('*/*')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + // some + lm.mmap_expr ("1-10/0-20", n++); + EXPECT_EQ (lm.to_string (), + "layer_map('+*/*';'+1-10/0-20')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); +} + diff --git a/src/edt/edt/edtEditorOptionsPages.cc b/src/edt/edt/edtEditorOptionsPages.cc index 5cf86b819..df2f67f69 100644 --- a/src/edt/edt/edtEditorOptionsPages.cc +++ b/src/edt/edt/edtEditorOptionsPages.cc @@ -69,8 +69,8 @@ static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le // ------------------------------------------------------------------ // EditorOptionsGeneric implementation -EditorOptionsGeneric::EditorOptionsGeneric (lay::Dispatcher *dispatcher) - : EditorOptionsPage (dispatcher) +EditorOptionsGeneric::EditorOptionsGeneric (lay::LayoutView *view, lay::Dispatcher *dispatcher) + : EditorOptionsPage (view, dispatcher) { mp_ui = new Ui::EditorOptionsGeneric (); mp_ui->setupUi (this); @@ -206,8 +206,8 @@ EditorOptionsGeneric::setup (lay::Dispatcher *root) // ------------------------------------------------------------------ // EditorOptionsText implementation -EditorOptionsText::EditorOptionsText (lay::Dispatcher *dispatcher) - : lay::EditorOptionsPage (dispatcher) +EditorOptionsText::EditorOptionsText (lay::LayoutView *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPage (view, dispatcher) { mp_ui = new Ui::EditorOptionsText (); mp_ui->setupUi (this); @@ -284,8 +284,8 @@ EditorOptionsText::setup (lay::Dispatcher *root) // ------------------------------------------------------------------ // EditorOptionsPath implementation -EditorOptionsPath::EditorOptionsPath (lay::Dispatcher *dispatcher) - : lay::EditorOptionsPage (dispatcher) +EditorOptionsPath::EditorOptionsPath (lay::LayoutView *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPage (view, dispatcher) { mp_ui = new Ui::EditorOptionsPath (); mp_ui->setupUi (this); @@ -385,8 +385,8 @@ EditorOptionsPath::setup (lay::Dispatcher *root) // ------------------------------------------------------------------ // EditorOptionsInst implementation -EditorOptionsInst::EditorOptionsInst (lay::Dispatcher *dispatcher) - : lay::EditorOptionsPage (dispatcher) +EditorOptionsInst::EditorOptionsInst (lay::LayoutView *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPage (view, dispatcher) { mp_ui = new Ui::EditorOptionsInst (); mp_ui->setupUi (this); @@ -440,14 +440,13 @@ EditorOptionsInst::update_cell_edits () } db::Layout *layout = 0; - lay::LayoutView *view = lay::LayoutView::current (); // find the layout the cell has to be looked up: that is either the layout of the current instance or // the library selected if (mp_ui->lib_cbx->current_library ()) { layout = &mp_ui->lib_cbx->current_library ()->layout (); - } else if (view && view->cellview (m_cv_index).is_valid ()) { - layout = &view->cellview (m_cv_index)->layout (); + } else if (view ()->cellview (m_cv_index).is_valid ()) { + layout = &view ()->cellview (m_cv_index)->layout (); } if (! layout) { @@ -485,7 +484,7 @@ EditorOptionsInst::browse_cell () { BEGIN_PROTECTED - if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { + if (m_cv_index >= 0 && view ()->cellview (m_cv_index).is_valid ()) { // find the layout the cell has to be looked up: that is either the layout of the current instance or // the library selected @@ -495,7 +494,7 @@ BEGIN_PROTECTED lib = mp_ui->lib_cbx->current_library (); layout = &lib->layout (); } else { - layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); + layout = &view ()->cellview (m_cv_index)->layout (); } bool all_cells = (mp_ui->lib_cbx->current_library () != 0 ? false : true); @@ -579,24 +578,36 @@ EditorOptionsInst::apply (lay::Dispatcher *root) root->config_set (cfg_edit_inst_place_origin, tl::to_string (place_origin)); } -void +void +EditorOptionsInst::technology_changed (const std::string &) +{ + // The layout's technology has changed + setup (dispatcher ()); +} + +void +EditorOptionsInst::active_cellview_changed () +{ + // The active cellview has changed + setup (dispatcher ()); +} + +void EditorOptionsInst::setup (lay::Dispatcher *root) { - m_cv_index = -1; - if (lay::LayoutView::current ()) { - m_cv_index = lay::LayoutView::current ()->active_cellview_index (); - } + m_cv_index = view ()->active_cellview_index (); try { mp_ui->lib_cbx->blockSignals (true); + std::string techname; + mp_ui->lib_cbx->update_list (); - if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { - mp_ui->lib_cbx->set_technology_filter (lay::LayoutView::current ()->cellview (m_cv_index)->tech_name (), true); - } else { - mp_ui->lib_cbx->set_technology_filter (std::string (), false); + if (m_cv_index >= 0 && view ()->cellview (m_cv_index).is_valid ()) { + techname = view ()->cellview (m_cv_index)->tech_name (); } + mp_ui->lib_cbx->set_technology_filter (techname, ! techname.empty ()); // cell name std::string s; @@ -606,7 +617,7 @@ EditorOptionsInst::setup (lay::Dispatcher *root) // library std::string l; root->config_get (cfg_edit_inst_lib_name, l); - mp_ui->lib_cbx->set_current_library (db::LibraryManager::instance ().lib_ptr_by_name (l)); + mp_ui->lib_cbx->set_current_library (db::LibraryManager::instance ().lib_ptr_by_name (l, techname)); mp_ui->lib_cbx->blockSignals (false); update_cell_edits (); @@ -667,8 +678,8 @@ EditorOptionsInst::setup (lay::Dispatcher *root) // ------------------------------------------------------------------ // EditorOptionsInstPCellParam implementation -EditorOptionsInstPCellParam::EditorOptionsInstPCellParam (lay::Dispatcher *dispatcher) - : lay::EditorOptionsPage (dispatcher), mp_pcell_parameters (0), mp_placeholder_label (0) +EditorOptionsInstPCellParam::EditorOptionsInstPCellParam (lay::LayoutView *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPage (view, dispatcher), mp_pcell_parameters (0), mp_placeholder_label (0) { mp_ui = new Ui::EditorOptionsInstPCellParam (); mp_ui->setupUi (this); @@ -693,11 +704,11 @@ EditorOptionsInstPCellParam::apply (lay::Dispatcher *root) std::string param; db::Layout *layout = 0; - db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name, view ()->active_cellview ().is_valid () ? view ()->active_cellview ()->tech_name () : std::string ()); if (lib) { layout = &lib->layout (); - } else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { - layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); + } else if (m_cv_index >= 0 && view ()->cellview (m_cv_index).is_valid ()) { + layout = &view ()->cellview (m_cv_index)->layout (); } bool ok = true; @@ -717,13 +728,16 @@ EditorOptionsInstPCellParam::apply (lay::Dispatcher *root) } } +void +EditorOptionsInstPCellParam::technology_changed (const std::string &) +{ + setup (dispatcher ()); +} + void EditorOptionsInstPCellParam::setup (lay::Dispatcher *root) { - m_cv_index = -1; - if (lay::LayoutView::current ()) { - m_cv_index = lay::LayoutView::current ()->active_cellview_index (); - } + m_cv_index = view ()->active_cellview_index (); bool needs_update = (mp_pcell_parameters == 0); @@ -743,7 +757,7 @@ EditorOptionsInstPCellParam::setup (lay::Dispatcher *root) needs_update = true; } - db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name, view ()->active_cellview ().is_valid () ? view ()->active_cellview ()->tech_name () : std::string ()); // pcell parameters std::string param; @@ -752,8 +766,8 @@ EditorOptionsInstPCellParam::setup (lay::Dispatcher *root) db::Layout *layout = 0; if (lib) { layout = &lib->layout (); - } else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) { - layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout (); + } else if (m_cv_index >= 0 && view ()->cellview (m_cv_index).is_valid ()) { + layout = &view ()->cellview (m_cv_index)->layout (); } std::vector pv; @@ -820,15 +834,14 @@ void EditorOptionsInstPCellParam::update_pcell_parameters (const std::vector ¶meters) { db::Layout *layout = 0; - lay::LayoutView *view = lay::LayoutView::current (); // find the layout the cell has to be looked up: that is either the layout of the current instance or // the library selected - db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name, view ()->active_cellview ().is_valid () ? view ()->active_cellview ()->tech_name () : std::string ()); if (lib) { layout = &lib->layout (); - } else if (view) { - const lay::CellView &cv = view->cellview (m_cv_index); + } else { + const lay::CellView &cv = view ()->cellview (m_cv_index); if (cv.is_valid ()) { layout = &cv->layout (); } @@ -856,10 +869,10 @@ EditorOptionsInstPCellParam::update_pcell_parameters (const std::vector pcell_declaration (pc.second) && view && view->cellview (m_cv_index).is_valid ()) { + if (pc.first && layout->pcell_declaration (pc.second) && view ()->cellview (m_cv_index).is_valid ()) { mp_pcell_parameters = new PCellParametersPage (this, true /*dense*/); - mp_pcell_parameters->setup (&view->cellview (m_cv_index)->layout (), view, m_cv_index, layout->pcell_declaration (pc.second), parameters); + mp_pcell_parameters->setup (view (), m_cv_index, layout->pcell_declaration (pc.second), parameters); this->layout ()->addWidget (mp_pcell_parameters); mp_pcell_parameters->set_state (pcp_state); diff --git a/src/edt/edt/edtEditorOptionsPages.h b/src/edt/edt/edtEditorOptionsPages.h index 3b639c5d8..92b697198 100644 --- a/src/edt/edt/edtEditorOptionsPages.h +++ b/src/edt/edt/edtEditorOptionsPages.h @@ -51,6 +51,7 @@ namespace lay { class PluginDeclaration; class Dispatcher; + class LayoutView; class Plugin; } @@ -68,7 +69,7 @@ class EditorOptionsGeneric Q_OBJECT public: - EditorOptionsGeneric (lay::Dispatcher *dispatcher); + EditorOptionsGeneric (lay::LayoutView *view, lay::Dispatcher *dispatcher); ~EditorOptionsGeneric (); virtual std::string title () const; @@ -91,7 +92,7 @@ class EditorOptionsText : public lay::EditorOptionsPage { public: - EditorOptionsText (lay::Dispatcher *dispatcher); + EditorOptionsText (lay::LayoutView *view, lay::Dispatcher *dispatcher); ~EditorOptionsText (); virtual std::string title () const; @@ -112,7 +113,7 @@ class EditorOptionsPath Q_OBJECT public: - EditorOptionsPath (lay::Dispatcher *dispatcher); + EditorOptionsPath (lay::LayoutView *view, lay::Dispatcher *dispatcher); ~EditorOptionsPath (); virtual std::string title () const; @@ -136,7 +137,7 @@ class EditorOptionsInst Q_OBJECT public: - EditorOptionsInst (lay::Dispatcher *root); + EditorOptionsInst (lay::LayoutView *view, lay::Dispatcher *root); ~EditorOptionsInst (); virtual std::string title () const; @@ -154,6 +155,9 @@ private: Ui::EditorOptionsInst *mp_ui; edt::PCellParametersPage *mp_pcell_parameters; int m_cv_index; + + virtual void technology_changed (const std::string &); + virtual void active_cellview_changed (); }; /** @@ -165,7 +169,7 @@ class EditorOptionsInstPCellParam Q_OBJECT public: - EditorOptionsInstPCellParam (lay::Dispatcher *root); + EditorOptionsInstPCellParam (lay::LayoutView *view, lay::Dispatcher *root); ~EditorOptionsInstPCellParam (); virtual std::string title () const; @@ -184,6 +188,7 @@ private: std::string m_lib_name, m_cell_name; void update_pcell_parameters (const std::vector ¶meters); + virtual void technology_changed (const std::string &); }; } diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index 08a4c27fa..40f805210 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -810,7 +810,7 @@ InstPropertiesPage::update_pcell_parameters () mp_pcell_parameters = new PCellParametersPage (pcell_tab); connect (mp_pcell_parameters, SIGNAL (edited ()), this, SIGNAL (edited ())); - mp_pcell_parameters->setup (&cv->layout (), mp_service->view (), pos->cv_index (), layout->pcell_declaration (pc.second), parameters); + mp_pcell_parameters->setup (mp_service->view (), pos->cv_index (), layout->pcell_declaration (pc.second), parameters); pcell_tab->layout ()->addWidget (mp_pcell_parameters); } diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index 01b667002..150f0f76d 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -25,6 +25,7 @@ #include "edtPropertiesPageUtils.h" #include "layWidgets.h" #include "layQtTools.h" +#include "layLayoutView.h" #include "tlScriptError.h" #include @@ -40,7 +41,7 @@ namespace edt { -static void set_value (const db::PCellParameterDeclaration &p, const db::Layout * /*layout*/, QWidget *widget, const tl::Variant &value) +static void set_value (const db::PCellParameterDeclaration &p, QWidget *widget, const tl::Variant &value) { if (p.get_choices ().empty ()) { @@ -153,8 +154,7 @@ PCellParametersPage::PCellParametersPage (QWidget *parent, bool dense) void PCellParametersPage::init () { - mp_pcell_decl = 0; - mp_layout = 0; + mp_pcell_decl.reset (0); mp_view = 0; m_cv_index = 0; mp_parameters_area = 0; @@ -183,10 +183,9 @@ PCellParametersPage::init () } void -PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters) +PCellParametersPage::setup (lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters) { - mp_pcell_decl = pcell_decl; - mp_layout = layout; + mp_pcell_decl.reset (const_cast (pcell_decl)); // no const weak_ptr ... mp_view = view; m_cv_index = cv_index; m_parameters = parameters; @@ -385,7 +384,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int } - set_value (*p, mp_layout, m_widgets.back (), value); + set_value (*p, m_widgets.back (), value); ++row; if (inner_frame == main_frame) { @@ -457,137 +456,144 @@ std::vector PCellParametersPage::get_parameters (bool *ok) { std::vector parameters; - bool edit_error = true; - int r = 0; - const std::vector &pcp = mp_pcell_decl->parameter_declarations (); - for (std::vector::const_iterator p = pcp.begin (); p != pcp.end (); ++p, ++r) { + try { - if (p->is_hidden () || p->get_type () == db::PCellParameterDeclaration::t_shape) { + if (! mp_pcell_decl) { + throw tl::Exception (tl::to_string (tr ("PCell no longer valid."))); + } - if (r < (int) m_parameters.size ()) { - parameters.push_back (m_parameters [r]); - } else { - parameters.push_back (p->get_default ()); - } + bool edit_error = true; - } else { + int r = 0; + const std::vector &pcp = mp_pcell_decl->parameter_declarations (); + for (std::vector::const_iterator p = pcp.begin (); p != pcp.end (); ++p, ++r) { - parameters.push_back (tl::Variant ()); + if (p->is_hidden () || p->get_type () == db::PCellParameterDeclaration::t_shape) { - if (p->get_choices ().empty ()) { - - switch (p->get_type ()) { - - case db::PCellParameterDeclaration::t_int: - { - QLineEdit *le = dynamic_cast (m_widgets [r]); - if (le) { - - try { - - int v = 0; - tl::from_string (tl::to_string (le->text ()), v); - - parameters.back () = tl::Variant (v); - lay::indicate_error (le, 0); - - } catch (tl::Exception &ex) { - - lay::indicate_error (le, &ex); - edit_error = false; - - } - - } - } - break; - - case db::PCellParameterDeclaration::t_double: - { - QLineEdit *le = dynamic_cast (m_widgets [r]); - if (le) { - - try { - - double v = 0; - tl::from_string (tl::to_string (le->text ()), v); - - parameters.back () = tl::Variant (v); - lay::indicate_error (le, 0); - - } catch (tl::Exception &ex) { - - lay::indicate_error (le, &ex); - edit_error = false; - - } - - } - } - break; - - case db::PCellParameterDeclaration::t_string: - { - QLineEdit *le = dynamic_cast (m_widgets [r]); - if (le) { - parameters.back () = tl::Variant (tl::to_string (le->text ())); - } - } - break; - - case db::PCellParameterDeclaration::t_list: - { - QLineEdit *le = dynamic_cast (m_widgets [r]); - if (le) { - std::vector values = tl::split (tl::to_string (le->text ()), ","); - parameters.back () = tl::Variant (values.begin (), values.end ()); - } - } - break; - - case db::PCellParameterDeclaration::t_layer: - { - lay::LayerSelectionComboBox *ly = dynamic_cast (m_widgets [r]); - if (ly) { - parameters.back () = tl::Variant (ly->current_layer_props ()); - } - } - break; - case db::PCellParameterDeclaration::t_boolean: - { - QCheckBox *cbx = dynamic_cast (m_widgets [r]); - if (cbx) { - parameters.back () = tl::Variant (cbx->isChecked ()); - } - } - break; - - default: - break; + if (r < (int) m_parameters.size ()) { + parameters.push_back (m_parameters [r]); + } else { + parameters.push_back (p->get_default ()); } } else { - QComboBox *cb = dynamic_cast (m_widgets [r]); - if (cb && cb->currentIndex () >= 0 && cb->currentIndex () < int (p->get_choices ().size ())) { - parameters.back () = p->get_choices () [cb->currentIndex ()]; + parameters.push_back (tl::Variant ()); + + if (p->get_choices ().empty ()) { + + switch (p->get_type ()) { + + case db::PCellParameterDeclaration::t_int: + { + QLineEdit *le = dynamic_cast (m_widgets [r]); + if (le) { + + try { + + int v = 0; + tl::from_string (tl::to_string (le->text ()), v); + + parameters.back () = tl::Variant (v); + lay::indicate_error (le, 0); + + } catch (tl::Exception &ex) { + + lay::indicate_error (le, &ex); + edit_error = false; + + } + + } + } + break; + + case db::PCellParameterDeclaration::t_double: + { + QLineEdit *le = dynamic_cast (m_widgets [r]); + if (le) { + + try { + + double v = 0; + tl::from_string (tl::to_string (le->text ()), v); + + parameters.back () = tl::Variant (v); + lay::indicate_error (le, 0); + + } catch (tl::Exception &ex) { + + lay::indicate_error (le, &ex); + edit_error = false; + + } + + } + } + break; + + case db::PCellParameterDeclaration::t_string: + { + QLineEdit *le = dynamic_cast (m_widgets [r]); + if (le) { + parameters.back () = tl::Variant (tl::to_string (le->text ())); + } + } + break; + + case db::PCellParameterDeclaration::t_list: + { + QLineEdit *le = dynamic_cast (m_widgets [r]); + if (le) { + std::vector values = tl::split (tl::to_string (le->text ()), ","); + parameters.back () = tl::Variant (values.begin (), values.end ()); + } + } + break; + + case db::PCellParameterDeclaration::t_layer: + { + lay::LayerSelectionComboBox *ly = dynamic_cast (m_widgets [r]); + if (ly) { + parameters.back () = tl::Variant (ly->current_layer_props ()); + } + } + break; + case db::PCellParameterDeclaration::t_boolean: + { + QCheckBox *cbx = dynamic_cast (m_widgets [r]); + if (cbx) { + parameters.back () = tl::Variant (cbx->isChecked ()); + } + } + break; + + default: + break; + } + + } else { + + QComboBox *cb = dynamic_cast (m_widgets [r]); + if (cb && cb->currentIndex () >= 0 && cb->currentIndex () < int (p->get_choices ().size ())) { + parameters.back () = p->get_choices () [cb->currentIndex ()]; + } + } } } - } - - try { - if (! edit_error) { throw tl::Exception (tl::to_string (tr ("There are errors. See the highlighted edit fields for details."))); } // coerce the parameters - mp_pcell_decl->coerce_parameters (*mp_layout, parameters); + if (mp_view->cellview (m_cv_index).is_valid ()) { + mp_pcell_decl->coerce_parameters (mp_view->cellview (m_cv_index)->layout (), parameters); + } set_parameters (parameters); mp_error_label->hide (); @@ -628,12 +634,16 @@ PCellParametersPage::get_parameters (bool *ok) void PCellParametersPage::set_parameters (const std::vector ¶meters) { + if (! mp_pcell_decl) { + return; + } + // write the changed value back size_t r = 0; const std::vector &pcp = mp_pcell_decl->parameter_declarations (); for (std::vector::const_iterator p = pcp.begin (); p != pcp.end (); ++p, ++r) { if (r < parameters.size () && m_widgets [r]) { - set_value (*p, mp_layout, m_widgets [r], parameters [r]); + set_value (*p, m_widgets [r], parameters [r]); } } } diff --git a/src/edt/edt/edtPCellParametersPage.h b/src/edt/edt/edtPCellParametersPage.h index 9786a2c8b..a180d4f9c 100644 --- a/src/edt/edt/edtPCellParametersPage.h +++ b/src/edt/edt/edtPCellParametersPage.h @@ -78,7 +78,7 @@ public: * @param pcell_decl The PCell declaration * @param parameters The parameter values to show (if empty, the default values are used) */ - void setup (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters); + void setup (lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type ¶meters); /** * @brief Gets the pages current state @@ -104,7 +104,7 @@ public: */ const db::PCellDeclaration *pcell_decl () const { - return mp_pcell_decl; + return mp_pcell_decl.get (); } /** @@ -122,9 +122,8 @@ private: QScrollArea *mp_parameters_area; QLabel *mp_error_label; QLabel *mp_error_icon; - const db::PCellDeclaration *mp_pcell_decl; + tl::weak_ptr mp_pcell_decl; std::vector m_widgets; - const db::Layout *mp_layout; lay::LayoutView *mp_view; int m_cv_index; db::pcell_parameters_type m_parameters; diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 1cf07b535..8f7aa14c2 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -75,7 +75,7 @@ void get_text_editor_options_pages (std::vector &ret, ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-text-param", &text_cfg_descriptors[0], &text_cfg_descriptors[sizeof (text_cfg_descriptors) / sizeof (text_cfg_descriptors[0])])); - ret.push_back (new edt::EditorOptionsText (dispatcher)); + ret.push_back (new edt::EditorOptionsText (view, dispatcher)); } static @@ -101,7 +101,7 @@ void get_path_editor_options_pages (std::vector &ret, ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-path-param", &path_cfg_descriptors[0], &path_cfg_descriptors[sizeof (path_cfg_descriptors) / sizeof (path_cfg_descriptors[0])])); - ret.push_back (new EditorOptionsPath (dispatcher)); + ret.push_back (new EditorOptionsPath (view, dispatcher)); } static @@ -145,8 +145,8 @@ void get_inst_editor_options_pages (std::vector &ret, ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-inst-param", &inst_cfg_descriptors[0], &inst_cfg_descriptors[sizeof (inst_cfg_descriptors) / sizeof (inst_cfg_descriptors[0])])); - ret.push_back (new EditorOptionsInstPCellParam (dispatcher)); - ret.push_back (new EditorOptionsInst (dispatcher)); + ret.push_back (new EditorOptionsInstPCellParam (view, dispatcher)); + ret.push_back (new EditorOptionsInst (view, dispatcher)); } template @@ -327,10 +327,10 @@ public: return false; } - virtual void get_editor_options_pages (std::vector &pages, lay::LayoutView * /*view*/, lay::Dispatcher *dispatcher) const + virtual void get_editor_options_pages (std::vector &pages, lay::LayoutView *view, lay::Dispatcher *dispatcher) const { // NOTE: we do not set plugin_declaration which makes the page unspecific - EditorOptionsGeneric *generic_opt = new EditorOptionsGeneric (dispatcher); + EditorOptionsGeneric *generic_opt = new EditorOptionsGeneric (view, dispatcher); pages.push_back (generic_opt); } diff --git a/src/edt/edt/edtRecentConfigurationPage.cc b/src/edt/edt/edtRecentConfigurationPage.cc index 554f85be0..a10d7739e 100644 --- a/src/edt/edt/edtRecentConfigurationPage.cc +++ b/src/edt/edt/edtRecentConfigurationPage.cc @@ -27,6 +27,7 @@ #include "layLayerTreeModel.h" #include "dbLibraryManager.h" #include "dbLibrary.h" +#include "tlLog.h" #include #include @@ -55,7 +56,7 @@ RecentConfigurationPage::init () ly->addWidget (mp_tree_widget); connect (mp_tree_widget, SIGNAL (itemClicked (QTreeWidgetItem *, int)), this, SLOT (item_clicked (QTreeWidgetItem *))); - mp_view->layer_list_changed_event.add (this, &RecentConfigurationPage::layers_changed); + view ()->layer_list_changed_event.add (this, &RecentConfigurationPage::layers_changed); mp_tree_widget->setColumnCount (int (m_cfg.size ())); @@ -89,16 +90,24 @@ RecentConfigurationPage::get_stored_values () const std::string serialized_list = dispatcher ()->config_get (m_recent_cfg_name); std::list > values; - tl::Extractor ex (serialized_list.c_str ()); - while (! ex.at_end ()) { - values.push_back (std::vector ()); - while (! ex.at_end () && ! ex.test (";")) { - values.back ().push_back (std::string ()); - ex.read_word_or_quoted (values.back ().back ()); - ex.test (","); + try { + + tl::Extractor ex (serialized_list.c_str ()); + while (! ex.at_end ()) { + + values.push_back (std::vector ()); + while (! ex.at_end () && ! ex.test (";")) { + values.back ().push_back (std::string ()); + ex.read_word_or_quoted (values.back ().back ()); + ex.test (","); + } + } + } catch (tl::Exception &ex) { + tl::error << tl::to_string (tr ("Error reading configuration item ")) << m_recent_cfg_name << ": " << ex.msg (); + values.clear (); } return values; @@ -158,7 +167,11 @@ RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std case RecentConfigurationPage::Bool: { bool f = false; - tl::from_string (values [column], f); + try { + tl::from_string (values [column], f); + } catch (tl::Exception &ex) { + tl::error << tl::to_string (tr ("Configuration error (ArrayFlag/Bool): ")) << ex.msg (); + } static QString checkmark = QString::fromUtf8 ("\xe2\x9c\x93"); item->setText (column, f ? checkmark : QString ()); // "checkmark" } @@ -166,10 +179,15 @@ RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std case RecentConfigurationPage::Layer: { - int icon_size = mp_view->style ()->pixelMetric (QStyle::PM_ButtonIconSize); - lay::LayerPropertiesConstIterator l = lp_iter_from_string (mp_view, values [column]); + int icon_size = view ()->style ()->pixelMetric (QStyle::PM_ButtonIconSize); + lay::LayerPropertiesConstIterator l; + try { + l = lp_iter_from_string (view (), values [column]); + } catch (tl::Exception &ex) { + tl::error << tl::to_string (tr ("Configuration error (Layer): ")) << ex.msg (); + } if (! l.is_null () && ! l.at_end ()) { - item->setIcon (column, lay::LayerTreeModel::icon_for_layer (l, mp_view, icon_size, icon_size, 0, true)); + item->setIcon (column, lay::LayerTreeModel::icon_for_layer (l, view (), icon_size, icon_size, 0, true)); item->setText (column, tl::to_qstring (values [column])); } else { item->setIcon (column, QIcon ()); @@ -199,7 +217,11 @@ RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std int flag_column = 0; for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++flag_column) { if (c->rendering == RecentConfigurationPage::ArrayFlag) { - tl::from_string (values [flag_column], is_array); + try { + tl::from_string (values [flag_column], is_array); + } catch (tl::Exception &ex) { + tl::error << tl::to_string (tr ("Configuration error (IntIfArray/DoubleIfArray): ")) << ex.msg (); + } break; } } @@ -219,7 +241,11 @@ RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std const db::Library *lib = 0; for (std::list::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++libname_column) { if (c->rendering == RecentConfigurationPage::CellLibraryName) { - lib = db::LibraryManager::instance ().lib_ptr_by_name (values [libname_column]); + if (view ()->active_cellview ().is_valid ()) { + lib = db::LibraryManager::instance ().lib_ptr_by_name (values [libname_column], view ()->active_cellview ()->tech_name ()); + } else { + lib = db::LibraryManager::instance ().lib_ptr_by_name (values [libname_column]); + } break; } } @@ -254,7 +280,11 @@ RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std case RecentConfigurationPage::PCellParameters: { std::map pcp; - pcp = pcell_parameters_from_string (values [column]); + try { + pcp = pcell_parameters_from_string (values [column]); + } catch (tl::Exception &ex) { + tl::error << tl::to_string (tr ("Configuration error (PCellParameters): ")) << ex.msg (); + } std::string r; for (std::map::const_iterator p = pcp.begin (); p != pcp.end (); ++p) { if (p != pcp.begin ()) { @@ -278,6 +308,12 @@ RecentConfigurationPage::layers_changed (int) update_list (get_stored_values ()); } +void +RecentConfigurationPage::technology_changed (const std::string &) +{ + update_list (get_stored_values ()); +} + void RecentConfigurationPage::update_list (const std::list > &stored_values) { @@ -327,7 +363,7 @@ RecentConfigurationPage::item_clicked (QTreeWidgetItem *item) ex.read (cv_index); } - mp_view->set_or_request_current_layer (cv_index, lp); + view ()->set_or_request_current_layer (cv_index, lp); } else { dispatcher ()->config_set (c->cfg_name, v); @@ -349,11 +385,11 @@ RecentConfigurationPage::commit_recent (lay::Dispatcher *root) std::string s; - if (!(mp_view->current_layer ().is_null () || mp_view->current_layer ().at_end ()) && mp_view->current_layer ()->is_visual ()) { + if (!(view ()->current_layer ().is_null () || view ()->current_layer ().at_end ()) && view ()->current_layer ()->is_visual ()) { - int cv_index = mp_view->current_layer ()->cellview_index (); - const lay::CellView &cv = mp_view->cellview (cv_index); - int li = mp_view->current_layer ()->layer_index (); + int cv_index = view ()->current_layer ()->cellview_index (); + const lay::CellView &cv = view ()->cellview (cv_index); + int li = view ()->current_layer ()->layer_index (); if (cv.is_valid () && cv->layout ().is_valid_layer (li)) { s = cv->layout ().get_properties (li).to_string (); if (cv_index > 0) { diff --git a/src/edt/edt/edtRecentConfigurationPage.h b/src/edt/edt/edtRecentConfigurationPage.h index a177be7fb..56b542950 100644 --- a/src/edt/edt/edtRecentConfigurationPage.h +++ b/src/edt/edt/edtRecentConfigurationPage.h @@ -46,8 +46,7 @@ class EditorOptionsPages; * @brief The base class for a object properties page */ class RecentConfigurationPage - : public lay::EditorOptionsPage, - public tl::Object + : public lay::EditorOptionsPage { Q_OBJECT @@ -79,7 +78,7 @@ public: template RecentConfigurationPage (lay::LayoutView *view, lay::Dispatcher *dispatcher, const std::string &recent_cfg_name, Iter begin_cfg, Iter end_cfg) - : EditorOptionsPage (dispatcher), mp_view (view), m_recent_cfg_name (recent_cfg_name), m_cfg (begin_cfg, end_cfg) + : EditorOptionsPage (view, dispatcher), m_recent_cfg_name (recent_cfg_name), m_cfg (begin_cfg, end_cfg) { init (); } @@ -96,7 +95,6 @@ private slots: void item_clicked (QTreeWidgetItem *item); private: - lay::LayoutView *mp_view; std::string m_recent_cfg_name; std::list m_cfg; QTreeWidget *mp_tree_widget; @@ -107,6 +105,7 @@ private: void set_stored_values (const std::list > &values) const; void render_to (QTreeWidgetItem *item, int column, const std::vector &values, RecentConfigurationPage::ConfigurationRendering rendering); void layers_changed (int); + virtual void technology_changed (const std::string &); }; } diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index c15bc6106..f2a627842 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -1414,7 +1414,7 @@ InstService::make_cell (const lay::CellView &cv) lay::LayerState layer_state = view ()->layer_snapshot (); - db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name, cv->tech_name ()); // find the layout the cell has to be looked up: that is either the layout of the current instance or // the library selected @@ -1853,8 +1853,13 @@ InstService::switch_cell_or_pcell (bool switch_parameters) } - db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); const lay::CellView &cv = view ()->cellview (m_cv_index); + db::Library *lib = 0; + if (cv.is_valid ()) { + lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name, cv->tech_name ()); + } else { + lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name); + } // find the layout the cell has to be looked up: that is either the layout of the current instance or // the library selected diff --git a/src/lay/lay/doc/about/layer_mapping.xml b/src/lay/lay/doc/about/layer_mapping.xml index 81d6205db..4376bc1e5 100644 --- a/src/lay/lay/doc/about/layer_mapping.xml +++ b/src/lay/lay/doc/about/layer_mapping.xml @@ -173,5 +173,58 @@ +

Multi-mapping and unmapping

+ +

+ Layer mapping table support an advanced feature which is to duplicate input layers to + a number of output layers (1:n) mapping. + The feature is enabled by prepending a "+" to the mapping statement. The following + statement will first select layer 5/0 and additionally copy it to layer 1000/0: +

+ +
5/0
++5/0: 1000/0
+
+ +

+ Unmapping removes the mapping for a specific layer or range. It is specified by prepending "-" + to the mapping expression. The following statement will map all datatypes of layer 5 to 0 except + for datatype 10 which is not considered. +

+ +
5/*: 5/0
+-5/10
+
+ +

+ Unmapping cancels the mappings specified previously, so the order of statements becomes important + when using unmapping and multi-mapping. +

+ +

Brackets

+ +

+ Square brackets can be used to imply mapping to the original layer. When putting square brackets + around a mapping expression, the default target is "*/*", which means expansion to the original layer. + Hence the following statements are identical: +

+ +
[1-10/*]
+1-10/* : */*
+
+ +

+ When combining this with "+" for multi-mapping, put "+" in front of the bracket. +

+ +

+ You can put round brackets around mapping expressions for visual clarity, specifically when + combining them with "-" (unmapping) or "+" (multi-mapping): +

+ +
-(1-10/*)
++(17/0 : 1017/0)
+
+ diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index c76f13667..8b4dff669 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -122,7 +122,7 @@ LibraryController::sync_files () m_file_watcher->enable (false); } - std::map > new_lib_files; + std::map new_lib_files; // build a list of paths vs. technology std::vector > paths; @@ -174,11 +174,11 @@ LibraryController::sync_files () QFileInfo fi (tl::to_qstring (lib_path)); bool needs_load = false; - std::map >::iterator ll = m_lib_files.find (lib_path); + std::map::iterator ll = m_lib_files.find (lib_path); if (ll == m_lib_files.end ()) { needs_load = true; } else { - if (fi.lastModified () > ll->second.second) { + if (fi.lastModified () > ll->second.time) { needs_load = true; } else { new_lib_files.insert (*ll); @@ -189,7 +189,9 @@ LibraryController::sync_files () std::auto_ptr lib (new db::Library ()); lib->set_description (filename); - lib->set_technology (p->second); + if (! p->second.empty ()) { + lib->set_technology (p->second); + } lib->set_name (tl::to_string (QFileInfo (*im).baseName ())); tl::log << "Reading library '" << lib_path << "'"; @@ -205,8 +207,19 @@ LibraryController::sync_files () } } - tl::log << "Registering as '" << lib->get_name () << "' for tech '" << p->second << "'"; - new_lib_files.insert (std::make_pair (lib_path, std::make_pair (lib->get_name (), fi.lastModified ()))); + if (! p->second.empty ()) { + tl::log << "Registering as '" << lib->get_name () << "' for tech '" << p->second << "'"; + } else { + tl::log << "Registering as '" << lib->get_name () << "'"; + } + + LibInfo li; + li.name = lib->get_name (); + li.time = fi.lastModified (); + if (! p->second.empty ()) { + li.tech.insert (p->second); + } + new_lib_files.insert (std::make_pair (lib_path, li)); db::LibraryManager::instance ().register_lib (lib.release ()); @@ -230,14 +243,14 @@ LibraryController::sync_files () std::set new_names; - for (std::map >::const_iterator lf = new_lib_files.begin (); lf != new_lib_files.end (); ++lf) { - new_names.insert (lf->second.first); + for (std::map::const_iterator lf = new_lib_files.begin (); lf != new_lib_files.end (); ++lf) { + new_names.insert (lf->second.name); } - for (std::map >::const_iterator lf = m_lib_files.begin (); lf != m_lib_files.end (); ++lf) { - if (new_names.find (lf->second.first) == new_names.end ()) { + for (std::map::const_iterator lf = m_lib_files.begin (); lf != m_lib_files.end (); ++lf) { + if (new_names.find (lf->second.name) == new_names.end ()) { try { - std::pair li = db::LibraryManager::instance ().lib_by_name (lf->second.first); + std::pair li = db::LibraryManager::instance ().lib_by_name (lf->second.name, lf->second.tech); if (li.first) { db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (li.second)); } diff --git a/src/lay/lay/layLibraryController.h b/src/lay/lay/layLibraryController.h index 86c0bfdcd..e07f50ade 100644 --- a/src/lay/lay/layLibraryController.h +++ b/src/lay/lay/layLibraryController.h @@ -119,9 +119,17 @@ private slots: void sync_with_external_sources (); private: + struct LibInfo + { + LibInfo () : name (), time (), tech () { } + std::string name; + QDateTime time; + std::set tech; + }; + tl::FileSystemWatcher *m_file_watcher; tl::DeferredMethod dm_sync_files; - std::map > m_lib_files; + std::map m_lib_files; void sync_files (); }; diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 425f8485f..c0770ab1b 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -2506,7 +2506,7 @@ void MainWindow::cm_new_layout () { std::string technology = m_initial_technology; - static std::string s_new_cell_cell_name; + static std::string s_new_cell_cell_name ("TOP"); static double s_new_cell_window_size = 2.0; double dbu = 0.0; diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc index 41b591104..e9ef8b94f 100644 --- a/src/lay/lay/laySearchReplaceDialog.cc +++ b/src/lay/lay/laySearchReplaceDialog.cc @@ -504,7 +504,7 @@ SearchReplaceResults::export_layout (db::Layout &layout) std::map::const_iterator lm = m_lp_map.find (layer); if (lm != m_lp_map.end ()) { - std::pair ll = insert_lm.logical (lm->second); + std::pair ll = insert_lm.first_logical (lm->second); if (! ll.first) { layer = layout.insert_layer (lm->second); insert_lm.map (lm->second, layer, lm->second); diff --git a/src/lay/lay/layTechnologyController.cc b/src/lay/lay/layTechnologyController.cc index d84876401..eec75c495 100644 --- a/src/lay/lay/layTechnologyController.cc +++ b/src/lay/lay/layTechnologyController.cc @@ -285,15 +285,29 @@ bool TechnologyController::menu_activated (const std::string &symbol) const { if (symbol == "technology_selector:apply_technology") { + if (lay::LayoutView::current () && lay::LayoutView::current ()->active_cellview ().is_valid ()) { - // Cancels the current modes - changing the technology may make libraries unavailable - // for example. + if (mp_mw) { - mp_mw->cancel (); + + // apply technology with undo + mp_mw->manager ().transaction (tl::sprintf (tl::to_string (tr ("Apply technology '%s'")), m_current_technology)); + try { + lay::LayoutView::current ()->active_cellview ()->apply_technology (m_current_technology); + mp_mw->manager ().commit (); + } catch (...) { + mp_mw->manager ().cancel (); + throw; + } + + } else { + lay::LayoutView::current ()->active_cellview ()->apply_technology (m_current_technology); } - lay::LayoutView::current ()->active_cellview ()->apply_technology (m_current_technology); + } + return true; + } else { return lay::PluginDeclaration::menu_activated (symbol); } diff --git a/src/laybasic/laybasic/LayerMappingWidget.ui b/src/laybasic/laybasic/LayerMappingWidget.ui index a343b33ce..03855fa9a 100644 --- a/src/laybasic/laybasic/LayerMappingWidget.ui +++ b/src/laybasic/laybasic/LayerMappingWidget.ui @@ -6,8 +6,8 @@ 0 0 - 446 - 205 + 536 + 290 @@ -29,6 +29,19 @@ 6 + + + + + 0 + 0 + + + + Load File + + + @@ -45,103 +58,114 @@ - - - - - 0 - 0 - - - - Load File - - - - - - - - 0 - 0 - - - - true - - - QAbstractItemView::InternalMove - - - QAbstractItemView::ExtendedSelection - - - true - - - false - - - - - - - Add a new layer to the list - - - Add - - - - :/add.png:/add.png - - - - - - - Delete the selected layers from the list - - - Delete - - - - :/clear.png:/clear.png - - - - - - - Qt::Vertical - - - - 20 - 5 - - - - - - - - Edit the current layer - - - Edit - - - - :/edit.png:/edit.png + + + + 0 + + + List + + + + + + + 0 + 0 + + + + true + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + true + + + false + + + + + + + Add a new layer to the list + + + Add + + + + :/add.png:/add.png + + + + + + + Delete the selected layers from the list + + + Delete + + + + :/clear.png:/clear.png + + + + + + + Edit the current layer + + + Edit + + + + :/edit.png:/edit.png + + + + + + + Qt::Vertical + + + + 20 + 5 + + + + + + + + + Text + + + + + + + - + diff --git a/src/laybasic/laybasic/layCellView.cc b/src/laybasic/laybasic/layCellView.cc index 0b28e12d0..1aacc1c67 100644 --- a/src/laybasic/laybasic/layCellView.cc +++ b/src/laybasic/laybasic/layCellView.cc @@ -59,6 +59,8 @@ LayoutHandle::LayoutHandle (db::Layout *layout, const std::string &filename) m_dirty (false), m_save_options_valid (false) { + layout->technology_changed_event.add (this, &LayoutHandle::on_technology_changed); + // layouts in the managed layouts space participate in spare proxy cleanup layout->do_cleanup (true); @@ -108,6 +110,12 @@ LayoutHandle::~LayoutHandle () file_watcher ().remove_file (filename ()); } +void +LayoutHandle::on_technology_changed () +{ + technology_changed_event (); +} + void LayoutHandle::layout_changed () { @@ -206,13 +214,20 @@ LayoutHandle::remove_ref () } } +const std::string & +LayoutHandle::tech_name () const +{ + static std::string s_empty; + return mp_layout ? mp_layout->technology_name () : s_empty; +} + const db::Technology * LayoutHandle::technology () const { - return db::Technologies::instance ()->technology_by_name (m_tech_name); + return mp_layout ? mp_layout->technology () : 0; } -void +void LayoutHandle::apply_technology (const std::string &tn) { set_tech_name (tn); @@ -223,16 +238,8 @@ LayoutHandle::apply_technology (const std::string &tn) void LayoutHandle::set_tech_name (const std::string &tn) { - if (tn != m_tech_name) { - if (db::Technologies::instance ()->has_technology (tn)) { - m_tech_name = tn; - } else { - m_tech_name = std::string (); - } - if (mp_layout) { - mp_layout->add_meta_info (db::MetaInfo ("technology", tl::to_string (tr ("Technology name")), tn)); - } - technology_changed_event (); + if (mp_layout && tn != tech_name ()) { + mp_layout->set_technology_name (tn); } } @@ -347,7 +354,7 @@ LayoutHandle::load (const db::LoadLayoutOptions &options, const std::string &tec // If there is no technology given and the reader reports one, use this one if (technology.empty ()) { - std::string tech_from_reader = layout ().meta_info_value ("technology"); + std::string tech_from_reader = layout ().technology_name (); if (! tech_from_reader.empty ()) { set_tech_name (tech_from_reader); } @@ -373,7 +380,7 @@ LayoutHandle::load () db::LayerMap new_lmap = reader.read (layout (), m_load_options); // Attach the technology from the reader if it reports one - std::string tech_from_reader = layout ().meta_info_value ("technology"); + std::string tech_from_reader = layout ().technology_name (); if (! tech_from_reader.empty ()) { set_tech_name (tech_from_reader); } diff --git a/src/laybasic/laybasic/layCellView.h b/src/laybasic/laybasic/layCellView.h index a1040dc6c..7d2ac54e4 100644 --- a/src/laybasic/laybasic/layCellView.h +++ b/src/laybasic/laybasic/layCellView.h @@ -115,10 +115,7 @@ public: * * An empty name indicates the default technology should be used. */ - const std::string &tech_name () const - { - return m_tech_name; - } + const std::string &tech_name () const; /** * @brief Applies the given technology @@ -300,12 +297,13 @@ private: int m_ref_count; std::string m_name; std::string m_filename; - std::string m_tech_name; bool m_dirty; db::SaveLayoutOptions m_save_options; bool m_save_options_valid; db::LoadLayoutOptions m_load_options; + void on_technology_changed (); + static std::map ms_dict; static tl::FileSystemWatcher *mp_file_watcher; }; diff --git a/src/laybasic/laybasic/layEditorOptionsPage.cc b/src/laybasic/laybasic/layEditorOptionsPage.cc index cc4a92d91..1d90960c6 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.cc +++ b/src/laybasic/laybasic/layEditorOptionsPage.cc @@ -24,6 +24,7 @@ #include "tlInternational.h" #include "layEditorOptionsPage.h" #include "layEditorOptionsPages.h" +#include "layLayoutView.h" namespace lay { @@ -31,10 +32,10 @@ namespace lay // ------------------------------------------------------------------ // EditorOptionsPage implementation -EditorOptionsPage::EditorOptionsPage (lay::Dispatcher *dispatcher) - : QWidget (0), mp_owner (0), m_active (true), mp_plugin_declaration (0), mp_dispatcher (dispatcher) +EditorOptionsPage::EditorOptionsPage (lay::LayoutView *view, lay::Dispatcher *dispatcher) + : QWidget (0), mp_owner (0), m_active (true), mp_plugin_declaration (0), mp_dispatcher (dispatcher), mp_view (view) { - // nothing yet .. + attach_events (); } EditorOptionsPage::~EditorOptionsPage () @@ -42,6 +43,30 @@ EditorOptionsPage::~EditorOptionsPage () set_owner (0); } +void +EditorOptionsPage::attach_events () +{ + detach_from_all_events (); + view ()->active_cellview_changed_event.add (this, &EditorOptionsPage::on_active_cellview_changed); + int cv_index = view ()->active_cellview_index (); + if (cv_index >= 0) { + view ()->cellview (cv_index)->technology_changed_event.add (this, &EditorOptionsPage::on_technology_changed); + } +} + +void +EditorOptionsPage::on_active_cellview_changed () +{ + active_cellview_changed (); + attach_events (); +} + +void +EditorOptionsPage::on_technology_changed () +{ + technology_changed (view ()->active_cellview_ref ()->tech_name ()); +} + void EditorOptionsPage::set_owner (EditorOptionsPages *owner) { diff --git a/src/laybasic/laybasic/layEditorOptionsPage.h b/src/laybasic/laybasic/layEditorOptionsPage.h index 3ee70b1a0..d6a6dad84 100644 --- a/src/laybasic/laybasic/layEditorOptionsPage.h +++ b/src/laybasic/laybasic/layEditorOptionsPage.h @@ -25,6 +25,8 @@ #include "laybasicCommon.h" +#include "tlObject.h" + #include namespace lay @@ -32,19 +34,21 @@ namespace lay class PluginDeclaration; class Dispatcher; +class LayoutView; class Plugin; +class CellView; class EditorOptionsPages; /** * @brief The base class for a object properties page */ class LAYBASIC_PUBLIC EditorOptionsPage - : public QWidget + : public QWidget, public tl::Object { Q_OBJECT public: - EditorOptionsPage (lay::Dispatcher *dispatcher); + EditorOptionsPage (lay::LayoutView *view, lay::Dispatcher *dispatcher); virtual ~EditorOptionsPage (); virtual std::string title () const = 0; @@ -72,11 +76,24 @@ protected: return mp_dispatcher; } + lay::LayoutView *view () const + { + return mp_view; + } + + virtual void active_cellview_changed () { } + virtual void technology_changed (const std::string & /*tech*/) { } + private: EditorOptionsPages *mp_owner; bool m_active; const lay::PluginDeclaration *mp_plugin_declaration; lay::Dispatcher *mp_dispatcher; + lay::LayoutView *mp_view; + + void on_active_cellview_changed (); + void on_technology_changed (); + void attach_events (); }; } diff --git a/src/laybasic/laybasic/layLayerMappingWidget.cc b/src/laybasic/laybasic/layLayerMappingWidget.cc index c5c243e77..e14dadcc7 100644 --- a/src/laybasic/laybasic/layLayerMappingWidget.cc +++ b/src/laybasic/laybasic/layLayerMappingWidget.cc @@ -53,6 +53,8 @@ LayerMappingWidget::LayerMappingWidget (QWidget *parent) mp_ui->layer_lv->viewport ()->acceptDrops (); + connect (mp_ui->tabs, SIGNAL (currentChanged (int)), this, SLOT (current_tab_changed (int))); + mp_layer_table_file_dialog = new lay::FileDialog (this, tl::to_string (QObject::tr ("Load Layer Table")), tl::to_string (QObject::tr ("Layer properties and text files (*.lyp *.txt);;Layer properties files (*.lyp);;Text files (*.txt);;All files (*)"))); @@ -72,6 +74,8 @@ LayerMappingWidget::set_layer_map (const db::LayerMap &lm) { std::vector layer_ids = lm.get_layers (); + mp_ui->text_edit->setPlainText (tl::to_qstring (lm.to_string_file_format ())); + mp_ui->layer_lv->reset (); mp_ui->layer_lv->clear (); @@ -88,16 +92,31 @@ LayerMappingWidget::set_layer_map (const db::LayerMap &lm) db::LayerMap LayerMappingWidget::get_layer_map () const +{ + return get_layer_map_from_tab (mp_ui->tabs->currentIndex ()); +} + +db::LayerMap +LayerMappingWidget::get_layer_map_from_tab (int tab) const { db::LayerMap lm; - for (int i = 0; i < mp_ui->layer_lv->count (); ++i) { - std::string t = tl::to_string (mp_ui->layer_lv->item (i)->data (Qt::DisplayRole).toString ()); - try { - lm.map_expr (t, (unsigned int) i); - } catch (...) { - mp_ui->layer_lv->setCurrentItem (mp_ui->layer_lv->item (i)); - throw; + + if (tab == 0) { + + for (int i = 0; i < mp_ui->layer_lv->count (); ++i) { + std::string t = tl::to_string (mp_ui->layer_lv->item (i)->data (Qt::DisplayRole).toString ()); + try { + lm.add_expr (t, (unsigned int) i); + } catch (...) { + mp_ui->layer_lv->setCurrentItem (mp_ui->layer_lv->item (i)); + throw; + } } + + } else { + + lm = db::LayerMap::from_string_file_format (tl::to_string (mp_ui->text_edit->toPlainText ())); + } return lm; @@ -128,17 +147,19 @@ LayerMappingWidget::load_button_pressed () mp_ui->layer_lv->reset (); mp_ui->layer_lv->clear (); + db::LayerMap lm; + // use those layers which have cellview index 0 + unsigned int n = 0; for (LayerPropertiesConstIterator lay_iter = props.begin_const_recursive (); ! lay_iter.at_end (); ++lay_iter) { if (! lay_iter->has_children () && lay_iter->source (true /*=real*/).cv_index () == 0) { db::LayerProperties db_lp = lay_iter->source (true /*=real*/).layer_props (); - QListWidgetItem *item = new QListWidgetItem (mp_ui->layer_lv); - item->setData (Qt::DisplayRole, tl::to_qstring (db_lp.to_string ())); - item->setFlags (item->flags () | Qt::ItemIsEditable); - mp_ui->layer_lv->addItem (item); + lm.map (db_lp, (unsigned int) n++); } } + set_layer_map (lm); + // if successful, stop now. success = true; @@ -230,5 +251,11 @@ LayerMappingWidget::edit_button_pressed () END_PROTECTED } +void +LayerMappingWidget::current_tab_changed (int index) +{ + set_layer_map (get_layer_map_from_tab (1 - index)); +} + } diff --git a/src/laybasic/laybasic/layLayerMappingWidget.h b/src/laybasic/laybasic/layLayerMappingWidget.h index 9252e8bb7..4c0bac3a4 100644 --- a/src/laybasic/laybasic/layLayerMappingWidget.h +++ b/src/laybasic/laybasic/layLayerMappingWidget.h @@ -84,11 +84,14 @@ private slots: void add_button_pressed (); void delete_button_pressed (); void edit_button_pressed (); + void current_tab_changed (int tab); private: lay::FileDialog *mp_layer_table_file_dialog; std::string m_layer_table_file; Ui::LayerMappingWidget *mp_ui; + + db::LayerMap get_layer_map_from_tab (int tab) const; }; } // namespace lay diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index fe3c642b7..18f1c6ddc 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -2538,7 +2538,7 @@ LayoutView::erase_cellview (unsigned int index) return; } - cancel (); + cancel_esc (); // issue to event that signals a change in the cellviews cellviews_about_to_change_event (); @@ -2704,6 +2704,8 @@ LayoutView::signal_apply_technology (lay::LayoutHandle *layout_handle) if (cellview (i).handle () == layout_handle) { + cancel_esc (); + std::string lyp_file; const db::Technology *tech = db::Technologies::instance ()->technology_by_name (cellview (i)->tech_name ()); if (tech && ! tech->eff_layer_properties_file ().empty ()) { @@ -3056,7 +3058,7 @@ void LayoutView::reload_layout (unsigned int cv_index) { stop (); - cancel (); + cancel_esc (); // save the current view state lay::DisplayState state; @@ -3948,6 +3950,13 @@ LayoutView::cancel () clear_selection (); } +void +LayoutView::cancel_esc () +{ + cancel (); + switch_mode (default_mode ()); +} + void LayoutView::bookmark_current_view () { @@ -4751,7 +4760,7 @@ LayoutView::select_cellviews_fit (const std::list &cvs) cellviews_about_to_change_event (); set_min_hier_levels (0); - cancel (); + cancel_esc (); m_cellviews = cvs; zoom_fit (); finish_cellviews_changed (); @@ -4772,6 +4781,12 @@ LayoutView::active_cellview_changed (int index) { if (m_active_cellview_changed_event_enabled) { + // we need to cancel pending drawing or dragging operations to reflect the new cellview (different target, may have different technology etc.) + cancel_esc (); + + // we need to setup the editor option pages because the technology may have changed + dm_setup_editor_option_pages (); + active_cellview_changed_event (); active_cellview_changed_with_index_event (index); @@ -4889,7 +4904,7 @@ LayoutView::select_cellviews (const std::list &cvs) cellviews_about_to_change_event (); set_min_hier_levels (0); - cancel (); + cancel_esc (); m_cellviews = cvs; redraw (); @@ -4914,7 +4929,7 @@ LayoutView::select_cellview (int index, const CellView &cv) cellview_about_to_change_event (index); - cancel (); + cancel_esc (); *cellview_iter (index) = cv; redraw (); diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index 66469b163..a54e731fb 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -2576,12 +2576,17 @@ public slots: void store_state (); /** - * @brief Cancel all edit operations and clear the selection + * @brief Cancels all edit operations, clears the selection and resets the mode to "Select" + */ + void cancel_esc (); + + /** + * @brief Cancels all edit operations and clears the selection */ void cancel (); /** - * @brief Cancel all edit operations but leave selection + * @brief Cancels all edit operations but maintains selection */ void cancel_edits (); diff --git a/src/laybasic/laybasic/layLibrariesView.cc b/src/laybasic/laybasic/layLibrariesView.cc index 5209b6f68..ec18a07c5 100644 --- a/src/laybasic/laybasic/layLibrariesView.cc +++ b/src/laybasic/laybasic/layLibrariesView.cc @@ -530,16 +530,28 @@ LibrariesView::do_update_content (int lib_index) size_t imin = (lib_index < 0 ? 0 : (size_t) lib_index); size_t imax = (lib_index < 0 ? std::numeric_limits ::max () : (size_t) lib_index); + std::string tech_name; // rebuild all events detach_from_all_events (); + + mp_view->active_cellview_changed_event.add (this, &LibrariesView::update_required); + lay::CellViewRef cv = mp_view->active_cellview_ref (); + if (cv.is_valid ()) { + cv->technology_changed_event.add (this, &LibrariesView::update_required); + tech_name = cv->tech_name (); + } + db::LibraryManager::instance ().changed_event.add (this, &LibrariesView::update_required); std::vector libraries; for (db::LibraryManager::iterator lib = db::LibraryManager::instance ().begin (); lib != db::LibraryManager::instance ().end (); ++lib) { - libraries.push_back (db::LibraryManager::instance ().lib (lib->second)); - libraries.back ()->layout ().hier_changed_event.add (this, &LibrariesView::update_required); - libraries.back ()->retired_state_changed_event.add (this, &LibrariesView::update_required); + db::Library *lib_ptr = db::LibraryManager::instance ().lib (lib->second); + if (! lib_ptr->for_technologies () || lib_ptr->is_for_technology (tech_name)) { + libraries.push_back (lib_ptr); + libraries.back ()->layout ().hier_changed_event.add (this, &LibrariesView::update_required); + libraries.back ()->retired_state_changed_event.add (this, &LibrariesView::update_required); + } } for (size_t i = imin; i < libraries.size () && i <= imax; ++i) { diff --git a/src/laybasic/laybasic/layWidgets.cc b/src/laybasic/laybasic/layWidgets.cc index 6ea947b02..c40d666b4 100644 --- a/src/laybasic/laybasic/layWidgets.cc +++ b/src/laybasic/laybasic/layWidgets.cc @@ -298,6 +298,7 @@ struct LayerSelectionComboBoxPrivateData const db::Layout *layout; lay::LayoutView *view; int cv_index; + db::LayerProperties last_props; }; LayerSelectionComboBox::LayerSelectionComboBox (QWidget *parent) @@ -419,11 +420,19 @@ LayerSelectionComboBox::set_view (lay::LayoutView *view, int cv_index, bool all_ return; } - mp_private->layout = &view->cellview (cv_index)->layout (); + mp_private->layout = 0; mp_private->view = view; mp_private->cv_index = cv_index; mp_private->all_layers = all_layers; + view->layer_list_changed_event.add (this, &LayerSelectionComboBox::on_layer_list_changed); + + update_layer_list (); +} + +void +LayerSelectionComboBox::on_layer_list_changed (int) +{ update_layer_list (); } @@ -442,7 +451,7 @@ void LayerSelectionComboBox::update_layer_list () { int i = currentIndex (); - db::LayerProperties props; + db::LayerProperties props = mp_private->last_props; if (i >= 0 && i < int (mp_private->layers.size ())) { props = mp_private->layers [i].first; } @@ -456,45 +465,60 @@ LayerSelectionComboBox::update_layer_list () if (mp_private->view) { - LPIPairCompareOp cmp_op; - std::map, std::string, LPIPairCompareOp> name_for_layer (cmp_op); - LayerPropertiesConstIterator lp = mp_private->view->begin_layers (); - while (! lp.at_end ()) { - if (lp->cellview_index () == mp_private->cv_index && ! lp->has_children () && (mp_private->all_layers || lp->layer_index () >= 0) && lp->source (true).layer_props () != db::LayerProperties ()) { - std::pair k (lp->source (true).layer_props (), lp->layer_index ()); - name_for_layer.insert (std::make_pair (k, lp->display_string (mp_private->view, true, true /*always show source*/))); - mp_private->layers.push_back (k); - } - ++lp; + const db::Layout *layout = 0; + + const CellView &cv = mp_private->view->cellview (mp_private->cv_index); + if (cv.is_valid ()) { + layout = & cv->layout (); } - size_t nk = mp_private->layers.size (); + if (! layout) { - for (unsigned int l = 0; l < mp_private->layout->layers (); ++l) { - if (mp_private->layout->is_valid_layer (l)) { - std::pair k (mp_private->layout->get_properties (l), int (l)); - if (name_for_layer.find (k) == name_for_layer.end ()) { + set_current_layer (-1); + + } else { + + LPIPairCompareOp cmp_op; + std::map, std::string, LPIPairCompareOp> name_for_layer (cmp_op); + LayerPropertiesConstIterator lp = mp_private->view->begin_layers (); + while (! lp.at_end ()) { + if (lp->cellview_index () == mp_private->cv_index && ! lp->has_children () && (mp_private->all_layers || lp->layer_index () >= 0) && lp->source (true).layer_props () != db::LayerProperties ()) { + std::pair k (lp->source (true).layer_props (), lp->layer_index ()); + name_for_layer.insert (std::make_pair (k, lp->display_string (mp_private->view, true, true /*always show source*/))); mp_private->layers.push_back (k); } + ++lp; } - } - std::sort (mp_private->layers.begin () + nk, mp_private->layers.end ()); + size_t nk = mp_private->layers.size (); - for (std::vector >::iterator ll = mp_private->layers.begin (); ll != mp_private->layers.end (); ++ll) { - std::map, std::string, LPIPairCompareOp>::const_iterator ln = name_for_layer.find (*ll); - if (ln != name_for_layer.end ()) { - addItem (tl::to_qstring (ln->second)); - } else { - addItem (tl::to_qstring (ll->first.to_string ())); + for (unsigned int l = 0; l < layout->layers (); ++l) { + if (layout->is_valid_layer (l)) { + std::pair k (layout->get_properties (l), int (l)); + if (name_for_layer.find (k) == name_for_layer.end ()) { + mp_private->layers.push_back (k); + } + } } - } - if (mp_private->new_layer_enabled) { - addItem (QObject::tr ("New Layer ..")); - } + std::sort (mp_private->layers.begin () + nk, mp_private->layers.end ()); - set_current_layer (props); + for (std::vector >::iterator ll = mp_private->layers.begin (); ll != mp_private->layers.end (); ++ll) { + std::map, std::string, LPIPairCompareOp>::const_iterator ln = name_for_layer.find (*ll); + if (ln != name_for_layer.end ()) { + addItem (tl::to_qstring (ln->second)); + } else { + addItem (tl::to_qstring (ll->first.to_string ())); + } + } + + if (mp_private->new_layer_enabled) { + addItem (QObject::tr ("New Layer ..")); + } + + set_current_layer (props); + + } } else if (mp_private->layout) { @@ -522,6 +546,8 @@ LayerSelectionComboBox::update_layer_list () void LayerSelectionComboBox::set_current_layer (const db::LayerProperties &props) { + mp_private->last_props = props; + for (std::vector >::iterator ll = mp_private->layers.begin (); ll != mp_private->layers.end (); ++ll) { if (ll->first.log_equal (props)) { setCurrentIndex (std::distance (mp_private->layers.begin (), ll)); @@ -562,7 +588,7 @@ LayerSelectionComboBox::current_layer_props () const { int i = currentIndex (); if (i < 0 || i > int (mp_private->layers.size ())) { - return db::LayerProperties (); + return mp_private->last_props; } else { return mp_private->layers [i].first; } @@ -600,7 +626,7 @@ LibrarySelectionComboBox::update_list () for (db::LibraryManager::iterator l = db::LibraryManager::instance ().begin (); l != db::LibraryManager::instance ().end (); ++l) { db::Library *lib = db::LibraryManager::instance ().lib (l->second); - if (! m_tech_set || !lib->for_technologies ()|| lib->is_for_technology (m_tech)) { + if (! m_tech_set || !lib->for_technologies () || lib->is_for_technology (m_tech)) { std::string item_text = lib->get_name (); if (! lib->get_description ().empty ()) { diff --git a/src/laybasic/laybasic/layWidgets.h b/src/laybasic/laybasic/layWidgets.h index 7de0fa33d..3f2002fb5 100644 --- a/src/laybasic/laybasic/layWidgets.h +++ b/src/laybasic/laybasic/layWidgets.h @@ -26,6 +26,8 @@ #include "laybasicCommon.h" +#include "tlObject.h" + #include #include #include @@ -170,7 +172,7 @@ private: * This combo box allows selecting a (physical) layer from a layout */ class LAYBASIC_PUBLIC LayerSelectionComboBox - : public QComboBox + : public QComboBox, public tl::Object { Q_OBJECT @@ -249,6 +251,7 @@ protected slots: private: LayerSelectionComboBoxPrivateData *mp_private; + void on_layer_list_changed (int); void update_layer_list (); }; diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc b/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc index 2f0f4f28d..057ff47f3 100644 --- a/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc +++ b/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc @@ -59,22 +59,20 @@ CIFReader::~CIFReader () const LayerMap & CIFReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) { - prepare_layers (); - const db::CIFReaderOptions &specific_options = options.get_options (); m_wire_mode = specific_options.wire_mode; m_dbu = specific_options.dbu; - db::LayerMap lm = specific_options.layer_map; - lm.prepare (layout); - set_layer_map (lm); + set_layer_map (specific_options.layer_map); set_create_layers (specific_options.create_other_layers); set_keep_layer_names (specific_options.keep_layer_names); + prepare_layers (layout); + do_read (layout); finish_layers (layout); - return layer_map (); + return layer_map_out (); } const LayerMap & diff --git a/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc b/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc index 7b74f0846..2d41920e8 100644 --- a/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc +++ b/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc @@ -39,13 +39,8 @@ static void run_test (tl::TestBase *_this, const std::string &base, const char * unsigned int ln = 0; tl::Extractor ex (map); while (! ex.at_end ()) { - std::string n; - int l; - ex.read_word_or_quoted (n); - ex.test (":"); - ex.read (l); + lm.add_expr (ex, ln++); ex.test (","); - lm.map (n, ln++, db::LayerProperties (l, 0)); } opt->layer_map = lm; opt->create_other_layers = true; @@ -173,6 +168,11 @@ TEST(3b) run_test (_this, tl::testsrc_private (), "t3.cif.gz", "t3b_au.gds.gz", "CAA:43,CCA:48,CCP:47,CMF:49,CMS:51,CPG:46,CSN:45,CSP:44,CVA:50,CWN:42,XP:26", 0.00012); } +TEST(3c) +{ + run_test (_this, tl::testsrc_private (), "t3.cif.gz", "t3c_au.gds.gz", "(CPG:1/0) +(CPG:1000/0) (CCP:1/0) (CMF:2/0) +(CMF:1000/0) (CVA:3/0)", 0.00012); +} + TEST(4) { run_test (_this, tl::testsrc_private (), "t4.cif.gz", "t4_au.gds.gz"); diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc index cb023d887..c0858a500 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc +++ b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc @@ -318,9 +318,7 @@ DXFReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) m_stream.reset (); m_initial = true; m_line_number = 0; - db::LayerMap lm = specific_options.layer_map; - lm.prepare (layout); - set_layer_map (lm); + set_layer_map (specific_options.layer_map); set_create_layers (specific_options.create_other_layers); set_keep_layer_names (specific_options.keep_layer_names); @@ -330,7 +328,7 @@ DXFReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) do_read (layout, top); cleanup (layout, top); - return layer_map (); + return layer_map_out (); } const LayerMap & @@ -366,43 +364,31 @@ DXFReader::warn (const std::string &msg) } } -std::pair -DXFReader::open_layer (db::Layout &layout, const std::string &n) -{ - if (n == zero_layer_name) { - return std::make_pair (true, m_zero_layer); - } else { - return NamedLayerReader::open_layer (layout, n); - } -} - void DXFReader::do_read (db::Layout &layout, db::cell_index_type top) { + prepare_layers (layout); + // create the zero layer - this is not mapped to GDS but can be specified in the layer mapping as // a layer named "0". - std::pair ll = layer_map ().logical (zero_layer_name, layout); - if (ll.first) { - // create the layer if it is not part of the layout yet. - if (! layout.is_valid_layer (ll.second)) { - layout.insert_layer (ll.second, layer_map ().mapping (ll.second)); - } + std::pair li = NamedLayerReader::open_layer (layout, zero_layer_name, true /*keep layer name*/, false /*don't create a new layer*/); + if (li.first) { - m_zero_layer = ll.second; + // we got one from the layer mapping + m_zero_layer = li.second; } else { - // or explicitly create the layer: - m_zero_layer = layer_map ().next_index (); - layout.insert_layer (m_zero_layer, db::LayerProperties (0, 0, zero_layer_name)); + // or we explicitly create the layer + db::LayerProperties lp_zero (0, 0, zero_layer_name); + m_zero_layer = layout.insert_layer (lp_zero); map_layer (zero_layer_name, m_zero_layer); } - prepare_layers (); + // Read sections - // Read sections int g; while (true) { diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h index 54881d2cd..beed6dfcf 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h +++ b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h @@ -193,7 +193,6 @@ private: void do_read (db::Layout &layout, db::cell_index_type top); - std::pair open_layer (db::Layout &layout, const std::string &n); db::cell_index_type make_layer_variant (db::Layout &layout, const std::string &cellname, db::cell_index_type template_cell, unsigned int layer, double sx, double sy); void cleanup (db::Layout &layout, db::cell_index_type top); diff --git a/src/plugins/streamers/dxf/unit_tests/dbDXFReader.cc b/src/plugins/streamers/dxf/unit_tests/dbDXFReaderTests.cc similarity index 100% rename from src/plugins/streamers/dxf/unit_tests/dbDXFReader.cc rename to src/plugins/streamers/dxf/unit_tests/dbDXFReaderTests.cc diff --git a/src/plugins/streamers/dxf/unit_tests/unit_tests.pro b/src/plugins/streamers/dxf/unit_tests/unit_tests.pro index de728685d..98d781faa 100644 --- a/src/plugins/streamers/dxf/unit_tests/unit_tests.pro +++ b/src/plugins/streamers/dxf/unit_tests/unit_tests.pro @@ -6,7 +6,7 @@ TARGET = dxf_tests include($$PWD/../../../../lib_ut.pri) SOURCES = \ - dbDXFReader.cc \ + dbDXFReaderTests.cc INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common diff --git a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.cc b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.cc index ee363ed67..d25c92bfc 100644 --- a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.cc +++ b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.cc @@ -51,23 +51,11 @@ GDS2ReaderText::~GDS2ReaderText() // .. nothing yet .. } -const LayerMap & -GDS2ReaderText::read (db::Layout &layout, const db::LoadLayoutOptions &options) +void +GDS2ReaderText::init (const db::LoadLayoutOptions &options) { + GDS2ReaderBase::init (options); storedRecId = 0; - - // HINT: reuse the standard GDS2 reader options for the text reader. - // However, the allow_big_records and allow_multi_xy_records options are ignored. - db::GDS2ReaderOptions gds2_options = options.get_options (); - db::CommonReaderOptions common_options = options.get_options (); - - return basic_read (layout, common_options.layer_map, common_options.create_other_layers, common_options.enable_text_objects, common_options.enable_properties, false, gds2_options.box_mode, common_options.cell_conflict_resolution); -} - -const LayerMap & -GDS2ReaderText::read (db::Layout &layout) -{ - return read (layout, db::LoadLayoutOptions ()); } void diff --git a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h index 56de72863..2ec5b278a 100644 --- a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h +++ b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h @@ -64,43 +64,14 @@ public: */ ~GDS2ReaderText(); - /** - * @brief The basic read method - * - * This method will read the stream data and translate this to - * insert calls into the layout object. This will not do much - * on the layout object beside inserting the objects. - * It can be given a couple of options specified with the - * LoadLayoutOptions object. - * The returned map will contain all layers, the passed - * ones and the newly created ones. - * - * @param layout The layout object to write to - * @param options The generic reader options - * @return The LayerMap object that tells where which layer was loaded - */ - virtual const LayerMap &read (db::Layout &layout, const LoadLayoutOptions &options); - - /** - * @brief The basic read method (without mapping) - * - * This method will read the stream data and translate this to - * insert calls into the layout object. This will not do much - * on the layout object beside inserting the objects. - * This version will read all input layers and return a map - * which tells which GDS2 layer has been read into which logical - * layer. - * - * @param layout The layout object to write to - * @return The LayerMap object - */ - virtual const LayerMap &read (db::Layout &layout); - /** * @brief Format */ const char *format () const { return "GDS2Text"; } +protected: + virtual void init (const LoadLayoutOptions &options); + private: tl::TextInputStream sStream; std::string sExtractedValue; diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc index 772da9429..8838dafa9 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc @@ -43,6 +43,7 @@ GDS2Reader::GDS2Reader (tl::InputStream &s) m_recptr (0), mp_rec_buf (0), m_stored_rec (0), + m_allow_big_records (true), m_progress (tl::to_string (tr ("Reading GDS2 file")), 10000) { m_progress.set_format (tl::to_string (tr ("%.0f MB"))); @@ -54,23 +55,16 @@ GDS2Reader::~GDS2Reader () // .. nothing yet .. } -const LayerMap & -GDS2Reader::read (db::Layout &layout, const db::LoadLayoutOptions &options) +void +GDS2Reader::init (const db::LoadLayoutOptions &options) { - m_options = options.get_options (); - m_common_options = options.get_options (); + GDS2ReaderBase::init (options); + + m_allow_big_records = options.get_options ().allow_big_records; m_recnum = 0; --m_recnum; m_reclen = 0; - - return basic_read (layout, m_common_options.layer_map, m_common_options.create_other_layers, m_common_options.enable_text_objects, m_common_options.enable_properties, m_options.allow_multi_xy_records, m_options.box_mode, m_common_options.cell_conflict_resolution); -} - -const LayerMap & -GDS2Reader::read (db::Layout &layout) -{ - return read (layout, db::LoadLayoutOptions ()); } void @@ -108,7 +102,7 @@ GDS2Reader::get_record () error (tl::to_string (tr ("Invalid record length (less than 4)"))); } if (m_reclen >= 0x8000) { - if (m_options.allow_big_records) { + if (m_allow_big_records) { warn (tl::to_string (tr ("Record length larger than 0x8000 encountered: interpreting as unsigned"))); } else { error (tl::to_string (tr ("Record length larger than 0x8000 encountered (reader is configured not to allow such records)"))); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h index 2b51144dd..21d1b5b05 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h @@ -72,43 +72,14 @@ public: */ ~GDS2Reader (); - /** - * @brief The basic read method - * - * This method will read the stream data and translate this to - * insert calls into the layout object. This will not do much - * on the layout object beside inserting the objects. - * It can be given a couple of options specified with the - * LoadLayoutOptions object. - * The returned map will contain all layers, the passed - * ones and the newly created ones. - * - * @param layout The layout object to write to - * @param options The generic reader options - * @return The LayerMap object that tells where which layer was loaded - */ - virtual const LayerMap &read (db::Layout &layout, const LoadLayoutOptions &options); - - /** - * @brief The basic read method (without mapping) - * - * This method will read the stream data and translate this to - * insert calls into the layout object. This will not do much - * on the layout object beside inserting the objects. - * This version will read all input layers and return a map - * which tells which GDS2 layer has been read into which logical - * layer. - * - * @param layout The layout object to write to - * @return The LayerMap object - */ - virtual const LayerMap &read (db::Layout &layout); - /** * @brief Format */ virtual const char *format () const { return "GDS2"; } +protected: + virtual void init (const LoadLayoutOptions &options); + private: tl::InputStream &m_stream; size_t m_recnum; @@ -117,8 +88,7 @@ private: unsigned char *mp_rec_buf; tl::string m_string_buf; short m_stored_rec; - db::GDS2ReaderOptions m_options; - db::CommonReaderOptions m_common_options; + bool m_allow_big_records; tl::AbsoluteProgress m_progress; virtual void error (const std::string &txt); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index d79793638..0c82d6a10 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -22,6 +22,7 @@ #include "dbGDS2ReaderBase.h" +#include "dbGDS2Format.h" #include "dbGDS2.h" #include "dbArray.h" @@ -34,42 +35,12 @@ namespace db // --------------------------------------------------------------- -/** - * @brief A utility class that maps the layers for the proxy cell recovery - */ -class GDS2ReaderLayerMapping - : public db::ImportLayerMapping -{ -public: - GDS2ReaderLayerMapping (db::GDS2ReaderBase *reader, db::Layout *layout, bool create) - : mp_reader (reader), mp_layout (layout), m_create (create) - { - // .. nothing yet .. - } - - std::pair map_layer (const db::LayerProperties &lprops) - { - // named layers that are imported from a library are ignored - if (lprops.is_named ()) { - return std::make_pair (false, 0); - } else { - return mp_reader->open_dl (*mp_layout, LDPair (lprops.layer, lprops.datatype), m_create); - } - } - -private: - db::GDS2ReaderBase *mp_reader; - db::Layout *mp_layout; - bool m_create; -}; - // --------------------------------------------------------------- // GDS2ReaderBase GDS2ReaderBase::GDS2ReaderBase () : m_dbu (0.001), m_dbuu (1.0), - m_create_layers (true), m_read_texts (true), m_read_properties (true), m_allow_multi_xy_records (false), @@ -83,31 +54,18 @@ GDS2ReaderBase::~GDS2ReaderBase () // .. nothing yet .. } -const LayerMap & -GDS2ReaderBase::basic_read (db::Layout &layout, const LayerMap &layer_map, bool create_other_layers, bool enable_text_objects, bool enable_properties, bool allow_multi_xy_records, unsigned int box_mode, db::CommonReader::CellConflictResolution cc_resolution) +void +GDS2ReaderBase::init (const db::LoadLayoutOptions &options) { - m_layer_map = layer_map; - m_layer_map.prepare (layout); - m_read_texts = enable_text_objects; - m_read_properties = enable_properties; + CommonReader::init (options); - m_allow_multi_xy_records = allow_multi_xy_records; - m_box_mode = box_mode; - m_create_layers = create_other_layers; + db::GDS2ReaderOptions gds2_options = options.get_options (); - set_cell_conflict_resolution (cc_resolution); + m_read_texts = common_options ().enable_text_objects; + m_read_properties = common_options ().enable_properties; - layout.start_changes (); - try { - do_read (layout); - finish (layout); - layout.end_changes (); - } catch (...) { - layout.end_changes (); - throw; - } - - return m_layer_map; + m_allow_multi_xy_records = gds2_options.allow_multi_xy_records; + m_box_mode = gds2_options.box_mode; } void @@ -181,34 +139,6 @@ GDS2ReaderBase::finish_element (db::PropertiesRepository &rep) } -std::pair -GDS2ReaderBase::open_dl (db::Layout &layout, const LDPair &dl, bool create) -{ - std::pair ll = m_layer_map.logical (dl, layout); - if (ll.first) { - - return ll; - - } else if (! create) { - - // layer not mapped and no layer create is requested - return ll; - - } else { - - // and create the layer - db::LayerProperties lp; - lp.layer = dl.layer; - lp.datatype = dl.datatype; - - unsigned int ll = layout.insert_layer (lp); - m_layer_map.map (dl, ll, lp); - - return std::make_pair (true, ll); - - } -} - inline db::Point pt_conv (const GDS2XY &p) { @@ -358,17 +288,21 @@ GDS2ReaderBase::do_read (db::Layout &layout) db::cell_index_type cell_index = make_cell (layout, m_cellname); - db::Cell *cell = &layout.cell (cell_index); - + bool ignore_cell = false; std::map >::const_iterator ctx = m_context_info.find (m_cellname); if (ctx != m_context_info.end ()) { - GDS2ReaderLayerMapping layer_mapping (this, &layout, m_create_layers); + CommonReaderLayerMapping layer_mapping (this, &layout); if (layout.recover_proxy_as (cell_index, ctx->second.begin (), ctx->second.end (), &layer_mapping)) { // ignore everything in that cell since it is created by the import: - cell = 0; + ignore_cell = true; } } + db::Cell *cell = 0; + if (! ignore_cell) { + cell = &layout.cell (cell_index); + } + long attr = 0; db::PropertiesRepository::properties_set cell_properties; @@ -553,7 +487,7 @@ GDS2ReaderBase::read_boundary (db::Layout &layout, db::Cell &cell, bool from_box unsigned int xy_length = 0; GDS2XY *xy_data = get_xy_data (xy_length); - std::pair ll = open_dl (layout, ld, m_create_layers); + std::pair ll = open_dl (layout, ld); if (ll.first) { // create a box object if possible @@ -734,7 +668,7 @@ GDS2ReaderBase::read_path (db::Layout &layout, db::Cell &cell) unsigned int xy_length = 0; GDS2XY *xy_data = get_xy_data (xy_length); - std::pair ll = open_dl (layout, ld, m_create_layers); + std::pair ll = open_dl (layout, ld); if (ll.first) { // this will copy the path: @@ -825,7 +759,7 @@ GDS2ReaderBase::read_text (db::Layout &layout, db::Cell &cell) std::pair ll (false, 0); if (m_read_texts) { - ll = open_dl (layout, ld, m_create_layers); + ll = open_dl (layout, ld); } rec_id = get_record (); @@ -947,7 +881,7 @@ GDS2ReaderBase::read_box (db::Layout &layout, db::Cell &cell) } ld.datatype = get_ushort (); - std::pair ll = open_dl (layout, ld, m_create_layers); + std::pair ll = open_dl (layout, ld); if (get_record () != sXY) { error (tl::to_string (tr ("XY record expected"))); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h index a094da9fb..d31c0c537 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h @@ -69,42 +69,20 @@ public: const std::string &libname () const { return m_libname; } protected: - /** - * @brief The basic read method - * - * This method will read the stream data and translate this to - * insert calls into the layout object. This will not do much - * on the layout object beside inserting the objects. - * It can be given a couple of options specified with the - * LoadLayoutOptions object. - * The returned map will contain all layers, the passed - * ones and the newly created ones. - * - * @param layout The layout object to write to - * @param layer_map The layer mapping on input - * @param create_other_layer A flag indicating whether to read all other layers - * @param enable_text_objects A flag indicating whether to read text objects - * @param enable_properties A flag indicating whether to read user properties - * @param allow_multi_xy_records If true, tries to check for multiple XY records for BOUNDARY elements - * @param box_mode How to treat BOX records (0: ignore, 1: as rectangles, 2: as boundaries, 3: error) - * @param cc_resolution The cell name conflict resolution mode - * @return The LayerMap object that tells where which layer was loaded - */ - const LayerMap &basic_read (db::Layout &layout, const LayerMap &layer_map, bool create_other_layers, bool enable_text_objects, bool enable_properties, bool allow_multi_xy_records, unsigned int box_mode, db::CommonReader::CellConflictResolution cc_resolution); - /** * @brief Accessor method to the current cellname */ const std::string &cellname () const { return m_cellname; } + virtual void do_read (db::Layout &layout); + virtual void init (const LoadLayoutOptions &options); + private: friend class GDS2ReaderLayerMapping; - LayerMap m_layer_map; std::string m_cellname; std::string m_libname; double m_dbu, m_dbuu; - bool m_create_layers; bool m_read_texts; bool m_read_properties; bool m_allow_multi_xy_records; @@ -119,9 +97,6 @@ private: void read_box (db::Layout &layout, db::Cell &cell); void read_ref (db::Layout &layout, db::Cell &cell, bool array, tl::vector &instances, tl::vector &insts_wp); - void do_read (db::Layout &layout); - - std::pair open_dl (db::Layout &layout, const LDPair &dl, bool create); std::pair finish_element (db::PropertiesRepository &rep); void finish_element (); diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc index 838260bf3..6a70c0821 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc @@ -332,7 +332,7 @@ TEST(2) db::Layout layout_piece (&m); layout_piece = layout; - std::pair jj = map_full.logical (pairs[i]); + std::pair jj = map_full.first_logical (pairs[i]); EXPECT_EQ (jj.first, true); for (unsigned int j = 0; j < layout_piece.layers(); ++j) { @@ -418,7 +418,7 @@ TEST(3_AdvancedMapping) EXPECT_EQ (lm_read.to_string_file_format (), "1/10 : 1/0\n" - "2/0-9,21-*\n" + "2/0-1 : 2/0\n" "1/0 : 1/0\n" "1/1 : 1/1\n" "1/20 : 1/1020\n" @@ -427,23 +427,103 @@ TEST(3_AdvancedMapping) "2/11 : 2/11\n" "42/42 : 142/42\n" "100/0 : 200/0\n" - "2/12-20 : */*\n" - "1/22-30 : 1/*+1000\n" - "1/2-9,11-19,31-* : */*\n" - "0/*;3-41/*;42/0-41,43-*;43-99/*;100/1-*;101-*/* : *+100/*\n" ); std::string fn_au (tl::testsrc () + "/testdata/gds/alm_au.gds"); db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); } +TEST(3_MultiMapping) +{ + db::Manager m (false); + db::Layout layout (&m); + + db::LoadLayoutOptions options; + db::LayerMap lm, lm_read; + + unsigned int n = 0; + lm.map_expr ("*/*: */*", n++); + lm.unmap_expr ("1-2/10"); + lm.mmap_expr ("1-2/10: *+100/*", n++); + lm.mmap_expr ("1/10;2/10: 12/1010", n++); + lm.mmap_expr ("1/0-1: */*+1000", n++); + options.get_options ().layer_map = lm; + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/alm.gds"); + db::Reader reader (file); + lm_read = reader.read (layout, options); + } + + EXPECT_EQ (lm_read.to_string_file_format (), + "+1-2/10 : 12/1010\n" + "+1/0 : 1/1000\n" + "+1/0 : 1/0\n" + "+1/1 : 1/1001\n" + "+1/1 : 1/1\n" + "+1/10 : 101/10\n" + "1/20 : 1/20\n" + "1/21 : 1/21\n" + "2/0 : 2/0\n" + "2/1 : 2/1\n" + "+2/10 : 102/10\n" + "2/11 : 2/11\n" + "42/42 : 42/42\n" + "100/0 : 100/0\n" + ); + + std::string fn_au (tl::testsrc () + "/testdata/gds/alm_au2.gds"); + db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); +} + +TEST(3_MultiMapping2) +{ + db::Manager m (false); + db::Layout layout (&m); + + db::LoadLayoutOptions options; + db::LayerMap lm, lm_read; + + unsigned int n = 0; + lm.add_expr ("(1/0:1/0)", n++); + lm.add_expr ("+(1/0:1000/0)", n++); + lm.add_expr ("(4/0:1/0)", n++); + lm.add_expr ("(2/0:2/0)", n++); + lm.add_expr ("+(2/0:1000/0)", n++); + lm.add_expr ("(3/0:3/0)", n++); + options.get_options ().layer_map = lm; + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/t10.gds"); + db::Reader reader (file); + lm_read = reader.read (layout, options); + } + + EXPECT_EQ (lm_read.to_string_file_format (), + "+1/0;4/0 : 1/0\n" + "+1-2/0 : 1000/0\n" + "+2/0 : 2/0\n" + "3/0 : 3/0\n" + "6/0 : 6/0\n" + "8/0 : 8/0\n" + "5/0 : 5/0\n" + "7/0 : 7/0\n" + "3/1 : 3/1\n" + "6/1 : 6/1\n" + "8/1 : 8/1\n" + ); + + std::string fn_au (tl::testsrc () + "/testdata/gds/alm_au3.gds"); + db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); +} + TEST(4_CollectModeRename) { db::Manager m (false); db::Layout layout (&m); db::LoadLayoutOptions options; - options.get_options ().cell_conflict_resolution = db::CommonReader::RenameCell; + options.get_options ().cell_conflict_resolution = db::RenameCell; { tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); @@ -467,7 +547,7 @@ TEST(4_CollectModeOverwrite) db::Layout layout (&m); db::LoadLayoutOptions options; - options.get_options ().cell_conflict_resolution = db::CommonReader::OverwriteCell; + options.get_options ().cell_conflict_resolution = db::OverwriteCell; { tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); @@ -491,7 +571,7 @@ TEST(4_CollectModeSkip) db::Layout layout (&m); db::LoadLayoutOptions options; - options.get_options ().cell_conflict_resolution = db::CommonReader::SkipNewCell; + options.get_options ().cell_conflict_resolution = db::SkipNewCell; { tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); @@ -515,7 +595,7 @@ TEST(4_CollectModeAdd) db::Layout layout (&m); db::LoadLayoutOptions options; - options.get_options ().cell_conflict_resolution = db::CommonReader::AddToCell; + options.get_options ().cell_conflict_resolution = db::AddToCell; { tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc index 4dc1d25ac..3917c4c85 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc @@ -157,14 +157,14 @@ DEFImporter::read_diearea (db::Layout &layout, db::Cell &design, double scale) if (points.size () >= 2) { // create outline shape - std::pair dl = open_layer (layout, std::string (), Outline, 0); - if (dl.first) { + std::set dl = open_layer (layout, std::string (), Outline, 0); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { if (points.size () == 2) { - design.shapes (dl.second).insert (db::Box (points [0], points [1])); + design.shapes (*l).insert (db::Box (points [0], points [1])); } else { db::Polygon p; p.assign_hull (points.begin (), points.end ()); - design.shapes (dl.second).insert (p); + design.shapes (*l).insert (p); } } @@ -305,9 +305,9 @@ DEFImporter::read_blockages (db::Layout &layout, db::Cell &design, double scale) db::Polygon p; read_polygon (p, scale); - std::pair dl = open_layer (layout, layer, layer.empty () ? PlacementBlockage : Blockage, 0); - if (dl.first) { - design.shapes (dl.second).insert (p); + std::set dl = open_layer (layout, layer, layer.empty () ? PlacementBlockage : Blockage, 0); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { + design.shapes (*l).insert (p); } } else if (test ("RECT")) { @@ -315,9 +315,9 @@ DEFImporter::read_blockages (db::Layout &layout, db::Cell &design, double scale) db::Polygon p; read_rect (p, scale); - std::pair dl = open_layer (layout, layer, layer.empty () ? PlacementBlockage : Blockage, 0); - if (dl.first) { - design.shapes (dl.second).insert (p); + std::set dl = open_layer (layout, layer, layer.empty () ? PlacementBlockage : Blockage, 0); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { + design.shapes (*l).insert (p); } } else { @@ -554,17 +554,19 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C test (")"); - std::pair dl = open_layer (layout, ln, specialnets ? SpecialRouting : Routing, mask); - if (dl.first) { + std::set dl = open_layer (layout, ln, specialnets ? SpecialRouting : Routing, mask); + if (! dl.empty ()) { db::Point p (x, y); db::Box rect (db::Point (db::DPoint ((x + x1) * scale, (y + y1) * scale)), db::Point (db::DPoint ((x + x2) * scale, (y + y2) * scale))); - if (prop_id != 0) { - design.shapes (dl.second).insert (db::object_with_properties (rect, prop_id)); - } else { - design.shapes (dl.second).insert (rect); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { + if (prop_id != 0) { + design.shapes (*l).insert (db::object_with_properties (rect, prop_id)); + } else { + design.shapes (*l).insert (rect); + } } } @@ -615,9 +617,9 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C } if (pts.size () > 1) { - std::pair dl = open_layer (layout, ln, specialnets ? SpecialRouting : Routing, mask); - if (dl.first) { - produce_routing_geometry (design, style, dl.second, prop_id, pts, ext, w); + std::set dl = open_layer (layout, ln, specialnets ? SpecialRouting : Routing, mask); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { + produce_routing_geometry (design, style, *l, prop_id, pts, ext, w); } } @@ -803,12 +805,12 @@ DEFImporter::read_nets (db::Layout &layout, db::Cell &design, double scale, bool db::Polygon p; read_polygon (p, scale); - std::pair dl = open_layer (layout, ln, specialnets ? SpecialRouting : Routing, mask); - if (dl.first) { + std::set dl = open_layer (layout, ln, specialnets ? SpecialRouting : Routing, mask); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { if (prop_id != 0) { - design.shapes (dl.second).insert (db::object_with_properties (p, prop_id)); + design.shapes (*l).insert (db::object_with_properties (p, prop_id)); } else { - design.shapes (dl.second).insert (p); + design.shapes (*l).insert (p); } } @@ -821,12 +823,12 @@ DEFImporter::read_nets (db::Layout &layout, db::Cell &design, double scale, bool db::Polygon p; read_rect (p, scale); - std::pair dl = open_layer (layout, ln, specialnets ? SpecialRouting : Routing, mask); - if (dl.first) { + std::set dl = open_layer (layout, ln, specialnets ? SpecialRouting : Routing, mask); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { if (prop_id != 0) { - design.shapes (dl.second).insert (db::object_with_properties (p, prop_id)); + design.shapes (*l).insert (db::object_with_properties (p, prop_id)); } else { - design.shapes (dl.second).insert (p); + design.shapes (*l).insert (p); } } @@ -1191,8 +1193,8 @@ DEFImporter::read_pins (db::Layout &layout, db::Cell &design, double scale) // Produce geometry collected so far for (std::map, std::vector >::const_iterator g = geometry.begin (); g != geometry.end (); ++g) { - std::pair dl = open_layer (layout, g->first.first, Pins, g->first.second); - if (dl.first) { + std::set dl = open_layer (layout, g->first.first, Pins, g->first.second); + if (! dl.empty ()) { db::properties_id_type prop_id = 0; if (produce_pin_props ()) { @@ -1204,21 +1206,27 @@ DEFImporter::read_pins (db::Layout &layout, db::Cell &design, double scale) for (std::vector::const_iterator p = g->second.begin (); p != g->second.end (); ++p) { db::Polygon pt = p->transformed (trans); if (prop_id == 0) { - design.shapes (dl.second).insert (pt); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { + design.shapes (*l).insert (pt); + } } else { - design.shapes (dl.second).insert (db::PolygonWithProperties (pt, prop_id)); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { + design.shapes (*l).insert (db::PolygonWithProperties (pt, prop_id)); + } } } } dl = open_layer (layout, g->first.first, Label, 0); - if (dl.first) { + if (! dl.empty ()) { db::Box bbox; if (! g->second.empty ()) { bbox = g->second.back ().box ().transformed (trans); } - design.shapes (dl.second).insert (db::Text (label.c_str (), db::Trans (db::Vector (bbox.center ())))); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { + design.shapes (*l).insert (db::Text (label.c_str (), db::Trans (db::Vector (bbox.center ())))); + } } } @@ -1554,10 +1562,10 @@ DEFImporter::do_read (db::Layout &layout) } else { - std::pair dl = open_layer (layout, std::string (), Regions, 0); - if (dl.first) { + std::set dl = open_layer (layout, std::string (), Regions, 0); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { for (std::vector::const_iterator p = r->second.begin (); p != r->second.end (); ++p) { - group_cell->shapes (dl.second).insert (*p); + group_cell->shapes (*l).insert (*p); } } regions.erase (r); @@ -1597,12 +1605,12 @@ DEFImporter::do_read (db::Layout &layout) if (! regions.empty ()) { - std::pair dl = open_layer (layout, std::string (), Regions, 0); - if (dl.first) { + std::set dl = open_layer (layout, std::string (), Regions, 0); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { for (std::map >::const_iterator r = regions.begin (); r != regions.end (); ++r) { for (std::vector::const_iterator p = r->second.begin (); p != r->second.end (); ++p) { - others_cell->shapes (dl.second).insert (*p); + others_cell->shapes (*l).insert (*p); } } diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index c4628b38c..c94a06568 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -43,11 +43,7 @@ std::string correct_path (const std::string &fn, const db::Layout &layout, const // if a technology is given and the file can be found in the technology's base path, take it // from there. - std::string tn = layout.meta_info_value ("technology"); - const db::Technology *tech = 0; - if (! tn.empty ()) { - tech = db::Technologies::instance ()->technology_by_name (tn); - } + const db::Technology *tech = layout.technology (); if (tech && ! tech->base_path ().empty ()) { std::string new_fn = tl::combine_path (tech->base_path (), fn); @@ -146,16 +142,16 @@ RuleBasedViaGenerator::create_cell (LEFDEFReaderState &reader, Layout &layout, d db::Vector vs ((m_cutsize.x () * m_columns + m_cutspacing.x () * (m_columns - 1)) / 2, (m_cutsize.y () * m_rows + m_cutspacing.y () * (m_rows - 1)) / 2); db::Box via_box (m_offset - vs, m_offset + vs); - std::pair dl (false, 0); + std::set dl; dl = reader.open_layer (layout, m_bottom_layer, ViaGeometry, mask_bottom); - if (dl.first) { - cell.shapes (dl.second).insert (db::Polygon (via_box.enlarged (m_be).moved (m_bo))); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { + cell.shapes (*l).insert (db::Polygon (via_box.enlarged (m_be).moved (m_bo))); } dl = reader.open_layer (layout, m_top_layer, ViaGeometry, mask_top); - if (dl.first) { - cell.shapes (dl.second).insert (db::Polygon (via_box.enlarged (m_te).moved (m_bo))); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { + cell.shapes (*l).insert (db::Polygon (via_box.enlarged (m_te).moved (m_bo))); } const char *p = m_pattern.c_str (); @@ -253,8 +249,8 @@ RuleBasedViaGenerator::create_cell (LEFDEFReaderState &reader, Layout &layout, d } dl = reader.open_layer (layout, m_cut_layer, ViaGeometry, cm); - if (dl.first) { - cell.shapes (dl.second).insert (db::Polygon (vb)); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { + cell.shapes (*l).insert (db::Polygon (vb)); } } @@ -322,9 +318,9 @@ GeometryBasedLayoutGenerator::create_cell (LEFDEFReaderState &reader, Layout &la unsigned int mshift = get_maskshift (g->first.first, ext_msl, masks); unsigned int mask = mask_for (g->first.first, g->first.second.second, mshift, nm); - std::pair dl = reader.open_layer (layout, g->first.first, g->first.second.first, mask); - if (dl.first) { - cell.shapes (dl.second).insert (g->second); + std::set dl = reader.open_layer (layout, g->first.first, g->first.second.first, mask); + for (std::set::const_iterator l = dl.begin (); l != dl.end (); ++l) { + cell.shapes (*l).insert (g->second); } } @@ -805,12 +801,24 @@ LEFDEFReaderState::register_layer (const std::string &ln) ++m_laynum; } -void -LEFDEFReaderState::map_layer_explicit (const std::string &n, LayerPurpose purpose, const db::LayerProperties &lp, unsigned int layer, unsigned int mask) +static bool try_read_layers (tl::Extractor &ex, std::vector &layers) { - tl_assert (m_has_explicit_layer_mapping); - m_layers [std::make_pair (n, std::make_pair (purpose, mask))] = std::make_pair (true, layer); - m_layer_map.map (lp, layer); + int l = 0; + if (! ex.try_read (l)) { + return false; + } + layers.push_back (l); + + if (ex.test (",")) { + do { + if (! ex.try_read (l)) { + return false; + } + layers.push_back (l); + } while (ex.test (",")); + } + + return true; } void @@ -832,13 +840,14 @@ LEFDEFReaderState::read_map_file (const std::string &path, db::Layout &layout) purpose_translation ["NET"] = Routing; purpose_translation ["VIA"] = ViaGeometry; purpose_translation ["BLOCKAGE"] = Blockage; + purpose_translation ["ALL"] = All; std::map purpose_translation_rev; for (std::map::const_iterator i = purpose_translation.begin (); i != purpose_translation.end (); ++i) { purpose_translation_rev.insert (std::make_pair (i->second, i->first)); } - std::map >, db::LayerProperties> layer_map; + std::map >, std::vector > layer_map; while (! ts.at_end ()) { @@ -852,113 +861,173 @@ LEFDEFReaderState::read_map_file (const std::string &path, db::Layout &layout) } else { std::string w1, w2; - int layer = 0, datatype = 0; + std::vector layers, datatypes; size_t max_purpose_str = 10; - if (ex.try_read_word (w1) && ex.try_read_word (w2, "._$,/:") && ex.try_read (layer) && ex.try_read (datatype)) { + if (! ex.try_read_word (w1) || ! ex.try_read_word (w2, "._$,/:") || ! try_read_layers (ex, layers) || ! try_read_layers (ex, datatypes)) { + tl::warn << tl::sprintf (tl::to_string (tr ("Reading layer map file %s, line %d not understood - skipped")), path, ts.line_number ()); + continue; + } - if (w1 == "DIEAREA") { + if (w1 == "DIEAREA") { - layer_map [std::make_pair (std::string (), std::make_pair (Outline, (unsigned int) 0))] = db::LayerProperties (layer, datatype, "OUTLINE"); + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + for (std::vector::const_iterator d = datatypes.begin (); d != datatypes.end (); ++d) { + layer_map [std::make_pair (std::string (), std::make_pair (Outline, (unsigned int) 0))].push_back (db::LayerProperties (*l, *d, "OUTLINE")); + } + } - } else if (w1 == "REGIONS") { + } else if (w1 == "REGIONS") { - layer_map [std::make_pair (std::string (), std::make_pair (Regions, (unsigned int) 0))] = db::LayerProperties (layer, datatype, "REGIONS"); + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + for (std::vector::const_iterator d = datatypes.begin (); d != datatypes.end (); ++d) { + layer_map [std::make_pair (std::string (), std::make_pair (Regions, (unsigned int) 0))].push_back (db::LayerProperties (*l, *d, "REGIONS")); + } + } - } else if (w1 == "BLOCKAGE") { + } else if (w1 == "BLOCKAGE") { - layer_map [std::make_pair (std::string (), std::make_pair (PlacementBlockage, (unsigned int) 0))] = db::LayerProperties (layer, datatype, "PLACEMENT_BLK"); + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + for (std::vector::const_iterator d = datatypes.begin (); d != datatypes.end (); ++d) { + layer_map [std::make_pair (std::string (), std::make_pair (PlacementBlockage, (unsigned int) 0))].push_back (db::LayerProperties (*l, *d, "PLACEMENT_BLK")); + } + } - } else if (w1 == "NAME") { + } else if (w1 == "NAME") { - // converts a line like - // "NAME M1/PINS,M2/PINS ..." - // into a canonical name mapping like - // "(M1/LABELS): M1.LABEL" - // "(M2/LABELS): M2.LABEL" + // converts a line like + // "NAME M1/PINS,M2/PINS ..." + // into a canonical name mapping like + // "(M1/LABELS): M1.LABEL" + // "(M2/LABELS): M2.LABEL" + + std::vector layer_names; + std::vector purposes = tl::split (w2, ","); + for (std::vector::const_iterator p = purposes.begin (); p != purposes.end (); ++p) { + if (*p == "DIEAREA" || *p == "ALL" || *p == "COMP") { + tl::warn << tl::sprintf (tl::to_string (tr ("Reading layer map file %s, line %d: NAME record ignored for entity: %s")), path, ts.line_number (), *p); + } else { + layer_names.push_back (tl::split (*p, "/").front ()); + } + } + + std::string final_name = tl::join (layer_names, "/") + ".LABEL"; + for (std::vector::const_iterator ln = layer_names.begin (); ln != layer_names.end (); ++ln) { + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + for (std::vector::const_iterator d = datatypes.begin (); d != datatypes.end (); ++d) { + layer_map [std::make_pair (*ln, std::make_pair (Label, (unsigned int) 0))].push_back (db::LayerProperties (*l, *d, final_name)); + } + } + } + + } else if (w1 == "COMP") { + + // ignore "COMP (ALL) ..." + tl::warn << tl::sprintf (tl::to_string (tr ("Reading layer map file %s, line %d: COMP entry ignored")), path, ts.line_number ()); + + } else { + + // converts a line like + // "M1 SPNET,NET,PINS,LEFPINS ..." + // into a canonical name mapping like + // "(M1,NET): M1.NET/PINS" + // "(M1,PINS): M1.NET/PINS" + // (separating, translating and recombing the purposes) + + std::set > translated_purposes; + + std::vector purposes = tl::split (w2, ","); + std::reverse (purposes.begin (), purposes.end ()); + + unsigned int mask = 0; + + for (std::vector::const_iterator p = purposes.begin (); p != purposes.end (); ++p) { + + std::string p_uc = tl::to_upper_case (*p); + tl::Extractor ex (p_uc.c_str ()); + + std::string ps; + ex.read_word_or_quoted (ps); + + std::map::const_iterator i = purpose_translation.find (ps); + if (i != purpose_translation.end ()) { + + if (i->second == Routing) { + + if (ex.test (":VOLTAGE:")) { + double f = 0.0; + ex.read (f); + tl::warn << tl::sprintf (tl::to_string (tr ("Reading layer map file %s, line %d: NET voltage constraint ignored for layer %s")), path, ts.line_number (), w1); + } + + } else if (i->second == ViaGeometry) { + + if (ex.test (":SIZE:")) { + std::string sz; + ex.read_word (sz); + tl::warn << tl::sprintf (tl::to_string (tr ("Reading layer map file %s, line %d: VIA size constraint ignored for layer %s")), path, ts.line_number (), w1); + } + + } - std::vector layers; - std::vector purposes = tl::split (w2, ","); - for (std::vector::const_iterator p = purposes.begin (); p != purposes.end (); ++p) { - layers.push_back (tl::split (*p, "/").front ()); } - std::string final_name = tl::join (layers, "/") + ".LABEL"; - for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { - layer_map [std::make_pair (*l, std::make_pair (Label, (unsigned int) 0))] = db::LayerProperties (layer, datatype, final_name); + if (ex.test (":MASK:")) { + ex.read (mask); } - } else if (w1 == "COMP") { + if (i == purpose_translation.end ()) { - // ignore "COMP (ALL) ..." + tl::warn << tl::sprintf (tl::to_string (tr ("Reading layer map file %s, line %d: purpose %s ignored for layer %s")), path, ts.line_number (), ps, w1); - } else { + } else if (i->second == All) { - // converts a line like - // "M1 SPNET,NET,PINS,LEFPINS ..." - // into a canonical name mapping like - // "(M1,NET): M1.NET/PINS" - // "(M1,PINS): M1.NET/PINS" - // (separating, translating and recombing the purposes) - - std::set > translated_purposes; - - std::vector purposes = tl::split (w2, ","); - std::reverse (purposes.begin (), purposes.end ()); - - unsigned int mask = 0; - - for (std::vector::const_iterator p = purposes.begin (); p != purposes.end (); ++p) { - - std::string p_uc = tl::to_upper_case (*p); - tl::Extractor ex (p_uc.c_str ()); - - std::string ps; - ex.read_word_or_quoted (ps); - - if (ex.test (":")) { - if (ex.test ("MASK") && ex.test (":")) { - ex.read (mask); + for (std::map::const_iterator p = purpose_translation.begin (); p != purpose_translation.end (); ++p) { + if (p->second != All) { + translated_purposes.insert (std::make_pair (p->second, mask)); } } - std::map::const_iterator i = purpose_translation.find (ps); - if (i != purpose_translation.end ()) { - translated_purposes.insert (std::make_pair (i->second, mask)); - } + } else { + + translated_purposes.insert (std::make_pair (i->second, mask)); } - // create a visual description string for the combined purposes - std::string purpose_str; + } - for (std::set >::const_iterator p = translated_purposes.begin (); p != translated_purposes.end (); ++p) { + // create a visual description string for the combined purposes + std::string purpose_str; - if (p != translated_purposes.begin ()) { - purpose_str += "/"; - } - - std::string ps = purpose_translation_rev [p->first]; - if (p->second > 0) { - ps += ":"; - ps += tl::to_string (p->second); - } - - if ((purpose_str + ps).size () > max_purpose_str) { - purpose_str += "..."; - break; - } else { - purpose_str += ps; - } + for (std::set >::const_iterator p = translated_purposes.begin (); p != translated_purposes.end (); ++p) { + if (p != translated_purposes.begin ()) { + purpose_str += "/"; } - std::string final_name = w1 + "." + purpose_str; - - for (std::set >::const_iterator p = translated_purposes.begin (); p != translated_purposes.end (); ++p) { - layer_map [std::make_pair (w1, *p)] = db::LayerProperties (layer, datatype, final_name); + std::string ps = purpose_translation_rev [p->first]; + if (p->second > 0) { + ps += ":"; + ps += tl::to_string (p->second); } + if ((purpose_str + ps).size () > max_purpose_str) { + purpose_str += "..."; + break; + } else { + purpose_str += ps; + } + + } + + std::string final_name = w1 + "." + purpose_str; + + for (std::set >::const_iterator p = translated_purposes.begin (); p != translated_purposes.end (); ++p) { + for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { + for (std::vector::const_iterator d = datatypes.begin (); d != datatypes.end (); ++d) { + layer_map [std::make_pair (w1, *p)].push_back (db::LayerProperties (*l, *d, final_name)); + } + } } } @@ -967,19 +1036,29 @@ LEFDEFReaderState::read_map_file (const std::string &path, db::Layout &layout) } + // build an explicit layer mapping now. + + tl_assert (m_has_explicit_layer_mapping); + m_layers.clear (); + m_layer_map.clear (); + db::DirectLayerMapping lm (&layout); - for (std::map >, db::LayerProperties>::const_iterator i = layer_map.begin (); i != layer_map.end (); ++i) { - map_layer_explicit (i->first.first, i->first.second.first, i->second, lm.map_layer (i->second).second, i->first.second.second); + for (std::map >, std::vector >::const_iterator i = layer_map.begin (); i != layer_map.end (); ++i) { + for (std::vector::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { + unsigned int layer = lm.map_layer (*j).second; + m_layers [i->first].insert (layer); + m_layer_map.mmap (*j, layer); + } } } -std::pair +std::set LEFDEFReaderState::open_layer (db::Layout &layout, const std::string &n, LayerPurpose purpose, unsigned int mask) { - std::map >, std::pair >::const_iterator nl = m_layers.find (std::make_pair (n, std::make_pair (purpose, mask))); + std::map >, std::set >::const_iterator nl = m_layers.find (std::make_pair (n, std::make_pair (purpose, mask))); if (nl == m_layers.end ()) { - std::pair ll (false, 0); + std::set ll; if (n.empty () || ! m_has_explicit_layer_mapping) { ll = open_layer_uncached (layout, n, purpose, mask); @@ -993,14 +1072,84 @@ LEFDEFReaderState::open_layer (db::Layout &layout, const std::string &n, LayerPu } } -std::pair -LEFDEFReaderState::open_layer_uncached (db::Layout &layout, const std::string &n, LayerPurpose purpose, unsigned int mask) +static std::string purpose_to_name (LayerPurpose purpose) +{ + switch (purpose) { + case Outline: + return "OUTLINE"; + case Regions: + return "REGION"; + case PlacementBlockage: + return "BLOCKAGE"; + case Routing: + return "NET"; + case SpecialRouting: + return "SPNET"; + case ViaGeometry: + return "VIA"; + case Label: + return "LABEL"; + case Pins: + return "PIN"; + case LEFPins: + return "LEFPIN"; + case Obstructions: + return "LEFOBS"; + case Blockage: + return "BLK"; + case All: + return "ALL"; + } + + return std::string (); +} + +/** + * @brief Implements implicit layer mapping + * + * This is how Implicit layer mapping works: + * + * 1. For named layers (e.g. routing, pin, etc. + * + * A decorated name is formed from the basic name and the purpose string (e.g. "M1" -> "M1.PIN"). + * With the example of "M1" and purpose Pin (decorated name "M1.PIN") and with a tech component datatype specification + * of "5" for "Pin", the layer map entries have the following effect: + * + * Layer map Result + * + * (nothing) M1.PIN (default/5) (only if "create_all_layers" is ON, "default" is a default number assigned by the reader) + * M1.PIN : 1/0 M1.PIN (1/0) + * M1.PIN : 1/17 M1.PIN (1/17) + * M1 : 1/0 M1.PIN (1/5) + * M1 : 1/2 M1.PIN (1/7) (datatypes will add) + * M1 M1.PIN (default/5) + * M1 : METAL1 METAL1.PIN (default/5) (name is taken from layer map and decorated) + * M1 : METAL1 (1/2) METAL1.PIN (1/7) + * M1.PIN : METAL1_PIN METAL1_PIN (default/5) (specific name is used without decoration) + * M1.PIN : METAL1_PIN (1/17) METAL1_PIN (1/17) (full and specific mapping) + * + * 2. For general layers (e.g. outline) + * + * By default, the name, layer and datatype are taken from the tech component's specification. The specification may + * lack the layer and datatype and even the name. If the name is missing, it is generated from the purpose. + * + * Here are some examples for the mapping of "OUTLINE": + * + * Tech component Layer map Result + * + * (nothing) (nothing) OUTLINE (only if "create_all_layers" is ON) + * OUTL (nothing) OUTL (default/0) ("default" is a default number assigned by the reader) + * OUTL (4/17) (nothing) OUTL (4/17) + * OUTL OUTL : 5/1 OUTL (5/1) + * OUTL (4/17) OUTL : 4/11 OUTL 4/11 + * OUTL (4/17) 4/17 : 4/11 OUTL 4/11 + * 4/17 4/17 : 4/11 OUTLINE 4/11 + */ + +std::set LEFDEFReaderState::open_layer_uncached(db::Layout &layout, const std::string &n, LayerPurpose purpose, unsigned int mask) { if (n.empty ()) { - // NOTE: the canonical name is independent from the tech component's settings - // as is "(name)". It's used for implementing the automatic map file import - // feature. std::string ld; bool produce = false; @@ -1016,7 +1165,7 @@ LEFDEFReaderState::open_layer_uncached (db::Layout &layout, const std::string &n } if (! produce) { - return std::make_pair (false, 0); + return std::set (); } db::LayerProperties lp; @@ -1028,13 +1177,69 @@ LEFDEFReaderState::open_layer_uncached (db::Layout &layout, const std::string &n lp.datatype = 0; } - for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { - if ((*l).second->log_equal (lp)) { - return std::make_pair (true, (*l).first); + // if no name is given, derive one from the purpose + if (lp.name.empty ()) { + lp.name = purpose_to_name (purpose); + } + + if (lp.layer < 0) { + std::map::const_iterator ldef = m_default_number.find (lp.name); + if (ldef != m_default_number.end ()) { + lp.layer = ldef->second; + lp.datatype = 0; } } - return std::make_pair (true, layout.insert_layer (lp)); + // employ the layer map to find the target layer + std::set ll = m_layer_map.logical (lp, layout); + if (ll.empty () && ! m_create_layers) { + return std::set (); + } + + std::set res; + + // map the layers to targets from the layout + // (NOTE: the other readers will do this in advance, but LEF/DEF is too dynamic) + + bool at_least_once = true; + for (std::set::const_iterator l = ll.begin (); l != ll.end () || at_least_once; ++l) { + + at_least_once = false; + + // If the layer map provides a target, use that one for the layer + db::LayerProperties lp_new = lp; + const db::LayerProperties *lpp = (l == ll.end () ? 0 : m_layer_map.target (*l)); + if (lpp) { + if (! lpp->name.empty ()) { + lp_new.name = lpp->name; + } + if (lpp->datatype >= 0) { + lp_new.datatype = lpp->datatype; + } + if (lpp->layer >= 0) { + lp_new.layer = lpp->layer; + } + } + + bool found = false; + for (db::Layout::layer_iterator i = layout.begin_layers (); i != layout.end_layers () && ! found; ++i) { + if ((*i).second->log_equal (lp_new)) { + found = true; + res.insert ((*i).first); + } + } + + if (! found) { + res.insert (layout.insert_layer (lp_new)); + } + + if (l == ll.end ()) { + break; + } + + } + + return res; } else { @@ -1068,12 +1273,10 @@ LEFDEFReaderState::open_layer_uncached (db::Layout &layout, const std::string &n break; } if (! produce) { - return std::make_pair (false, 0); + return std::set (); } } - // Note: "name" is the decorated name as provided by the tech component's - // x_suffix specifications. std::string name_suffix; int dt = 0; @@ -1115,8 +1318,10 @@ LEFDEFReaderState::open_layer_uncached (db::Layout &layout, const std::string &n } } + // "name" is the decorated name as provided by the tech component's x_suffix specifications. std::string name = n + name_suffix; + // Assign a layer number (a default one for now) and the datatype from the tech component's x_datatype specification. db::LayerProperties lp (name); lp.datatype = dt; std::map::const_iterator ldef = m_default_number.find (n); @@ -1124,37 +1329,71 @@ LEFDEFReaderState::open_layer_uncached (db::Layout &layout, const std::string &n lp.layer = ldef->second; } - std::pair ll = m_layer_map.logical (name, layout); - if (! ll.first) { + // Route the layer through the layer map, first the decorated name and if there is no mapping, the + // undecorated one. + std::set ll = m_layer_map.logical (name, layout); + bool generic_match = false; + if (ll.empty ()) { ll = m_layer_map.logical (n, layout); + generic_match = true; + } else if (n == name) { + // no suffix defined in tech component -> treat as generic match and combine datatypes + generic_match = true; } - if (ll.first) { + if (ll.empty () && ! m_create_layers) { + return std::set (); + } - const db::LayerProperties *lpp = m_layer_map.target (ll.second); + std::set res; + + bool at_least_once = true; + for (std::set::const_iterator l = ll.begin (); l != ll.end () || at_least_once; ++l) { + + at_least_once = false; + + // If the layer map provides a target, use that one for the layer + db::LayerProperties lp_new = lp; + const db::LayerProperties *lpp = (l == ll.end () ? 0 : m_layer_map.target (*l)); if (lpp) { - lp = *lpp; - if (lp.datatype >= 0) { - lp.datatype += dt; + lp_new = *lpp; + if (lp_new.datatype < 0) { + lp_new.datatype = dt; + } else if (generic_match) { + lp_new.datatype += dt; } - if (lp.name.empty ()) { - lp.name = name; + if (lp_new.name.empty ()) { + lp_new.name = name; + } else if (generic_match) { + lp_new.name += name_suffix; } } - } else if (! m_create_layers) { - return std::make_pair (false, 0); - } - - if (lp.layer >= 0 && lp.datatype >= 0) { - for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { - if ((*l).second->log_equal (lp)) { - return std::make_pair (true, (*l).first); + int lfound = -1; + if (lp_new.layer >= 0 && lp_new.datatype >= 0) { + for (db::Layout::layer_iterator i = layout.begin_layers (); i != layout.end_layers () && lfound < 0; ++i) { + if ((*i).second->log_equal (lp_new)) { + lfound = int ((*i).first); + } } } + + if (lfound < 0) { + res.insert (layout.insert_layer (lp_new)); + } else { + res.insert ((unsigned int) lfound); + db::LayerProperties lp_org = layout.get_properties ((unsigned int) lfound); + join_layer_names (lp_org.name, name); + layout.set_properties ((unsigned int) lfound, lp_org); + } + + if (l == ll.end ()) { + break; + } + } - return std::make_pair (true, layout.insert_layer (lp)); + return res; } } @@ -1181,78 +1420,13 @@ LEFDEFReaderState::finish (db::Layout &layout) db::LayerMap lm; - for (std::map >, std::pair >::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { + for (std::map >, std::set >::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { - if (! l->second.first) { + if (l->second.empty ()) { continue; } - std::string ps; - - switch (l->first.second.first) { - case Outline: - ps = "OUTLINE"; - break; - case Regions: - ps = "REGION"; - break; - case PlacementBlockage: - ps = "PLACEMENT_BLK"; - break; - case Routing: - default: - ps = "NET"; - break; - case SpecialRouting: - ps = "SPNET"; - break; - case ViaGeometry: - ps = "VIA"; - break; - case Label: - ps = "LABEL"; - break; - case Pins: - ps = "PIN"; - break; - case LEFPins: - ps = "LEFPIN"; - break; - case Obstructions: - ps = "OBS"; - break; - case Blockage: - ps = "BLK"; - break; - } - - unsigned int layer_index = l->second.second; - db::LayerProperties lp = layout.get_properties (layer_index); - - if (lp.layer < 0) { - - std::map::const_iterator n4n = number_for_name.end (); - if (! l->first.first.empty ()) { - n4n = number_for_name.find (l->first.first); - } - - if (n4n == number_for_name.end ()) { - do { - ++lnum; - } while (used_numbers.find (lnum) != used_numbers.end ()); - number_for_name.insert (std::make_pair (l->first.first, lnum)); - lp.layer = lnum; - } else { - lp.layer = n4n->second; - } - - } - - if (lp.datatype < 0) { - lp.datatype = 0; - } - - layout.set_properties (layer_index, lp); + std::string ps = purpose_to_name (l->first.second.first); std::string n = l->first.first; if (! n.empty ()) { @@ -1265,11 +1439,43 @@ LEFDEFReaderState::finish (db::Layout &layout) n += tl::to_string (l->first.second.second); } - lm.map (db::LayerProperties (n), l->second.second, lp); + for (std::set::const_iterator li = l->second.begin (); li != l->second.end (); ++li) { + + unsigned int layer_index = *li; + db::LayerProperties lp = layout.get_properties (layer_index); + + if (lp.layer < 0) { + + std::map::const_iterator n4n = number_for_name.end (); + if (! l->first.first.empty ()) { + n4n = number_for_name.find (l->first.first); + } + + if (n4n == number_for_name.end ()) { + do { + ++lnum; + } while (used_numbers.find (lnum) != used_numbers.end ()); + number_for_name.insert (std::make_pair (l->first.first, lnum)); + lp.layer = lnum; + } else { + lp.layer = n4n->second; + } + + } + + if (lp.datatype < 0) { + lp.datatype = 0; + } + + layout.set_properties (layer_index, lp); + + lm.mmap (db::LayerProperties (n), layer_index, lp); + + } } - // On return we deliver the "canonical" map + // On return we deliver the "canonical" map which lists the decorated name vs. the real ones. m_layer_map = lm; } diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index f29326547..85bd52395 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -775,7 +775,7 @@ public: } /** - * @brief Specify the LEF macro resolution strategy + * @brief Specify the LEF macro resolution strategy when reading DEF files * Values are: * 0: propduce LEF geometry unless a FOREIGN cell is specified (default) * 1: produce LEF geometry always and ignore FOREIGN @@ -884,6 +884,7 @@ enum LayerPurpose Blockage, // from DEF only PlacementBlockage, // from DEF only Regions, // from DEF only + All // from DEF only }; /** @@ -1053,7 +1054,7 @@ public: /** * @brief Create a new layer or return the index of the given layer */ - std::pair open_layer (db::Layout &layout, const std::string &name, LayerPurpose purpose, unsigned int mask); + std::set open_layer (db::Layout &layout, const std::string &name, LayerPurpose purpose, unsigned int mask); /** * @brief Registers a layer (assign a new default layer number) @@ -1187,7 +1188,7 @@ private: LEFDEFReaderState (const LEFDEFReaderState &); LEFDEFReaderState &operator= (const LEFDEFReaderState &); - std::map >, std::pair > m_layers; + std::map >, std::set > m_layers; db::LayerMap m_layer_map; bool m_create_layers; bool m_has_explicit_layer_mapping; @@ -1200,8 +1201,7 @@ private: std::map m_macro_generators; std::map m_foreign_cells; - std::pair open_layer_uncached (db::Layout &layout, const std::string &name, LayerPurpose purpose, unsigned int mask); - void map_layer_explicit (const std::string &n, LayerPurpose purpose, const LayerProperties &lp, unsigned int layer, unsigned int mask); + std::set open_layer_uncached (db::Layout &layout, const std::string &name, LayerPurpose purpose, unsigned int mask); db::cell_index_type foreign_cell(Layout &layout, const std::string &name); }; @@ -1363,7 +1363,7 @@ protected: /** * @brief Create a new layer or return the index of the given layer */ - std::pair open_layer (db::Layout &layout, const std::string &name, LayerPurpose purpose, unsigned int mask) + std::set open_layer (db::Layout &layout, const std::string &name, LayerPurpose purpose, unsigned int mask) { return mp_reader_state->open_layer (layout, name, purpose, mask); } diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index c874ab814..470e87d19 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -113,22 +113,25 @@ private: const db::LayerMap &read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &options, bool import_lef) { const db::LEFDEFReaderOptions *lefdef_options = dynamic_cast (options.get_options (format ())); - static db::LEFDEFReaderOptions default_options; - if (! lefdef_options) { - lefdef_options = &default_options; + db::LEFDEFReaderOptions effective_options; + if (lefdef_options) { + effective_options = *lefdef_options; } - db::LEFDEFReaderState state (lefdef_options, layout, tl::dirname (m_stream.absolute_path ())); + db::LEFDEFReaderState state (&effective_options, layout, tl::dirname (m_stream.absolute_path ())); - layout.dbu (lefdef_options->dbu ()); + layout.dbu (effective_options.dbu ()); if (import_lef) { + // Always produce LEF geometry when reading LEF + effective_options.set_macro_resolution_mode (1); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file"))); db::LEFImporter importer; - for (std::vector::const_iterator l = lefdef_options->begin_lef_files (); l != lefdef_options->end_lef_files (); ++l) { + for (std::vector::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { std::string lp = correct_path (*l, layout, tl::dirname (m_stream.absolute_path ())); @@ -149,7 +152,7 @@ private: DEFImporter importer; - for (std::vector::const_iterator l = lefdef_options->begin_lef_files (); l != lefdef_options->end_lef_files (); ++l) { + for (std::vector::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { std::string lp = correct_path (*l, layout, tl::dirname (m_stream.absolute_path ())); @@ -163,7 +166,7 @@ private: // Additionally read all LEF files next to the DEF file - if (lefdef_options->read_lef_with_def ()) { + if (effective_options.read_lef_with_def ()) { std::string input_dir = tl::absolute_path (m_stream.absolute_path ()); @@ -198,7 +201,7 @@ private: std::map foreign_cells = state.foreign_cells (); db::cell_index_type seen = std::numeric_limits::max (); - std::vector macro_layouts = lefdef_options->macro_layouts (); + std::vector macro_layouts = effective_options.macro_layouts (); for (std::vector::const_iterator m = macro_layouts.begin (); m != macro_layouts.end (); ++m) { std::vector target_cells, source_cells; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc index c1a6caa1c..00be1e947 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc @@ -937,6 +937,24 @@ LEFImporter::read_macro (Layout &layout) expect ("END"); + } else if (test ("DENSITY")) { + + // read over DENSITY statements + while (! test ("END")) { + if (test ("LAYER")) { + get (); + expect (";"); + } else { + expect ("RECT"); + for (int i = 0; i < 5; ++i) { + get_double (); + } + expect (";"); + } + } + + expect ("END"); + } else if (test ("FIXEDMASK")) { mg->set_fixedmask (true); diff --git a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc index b86a4fd21..2643028f6 100644 --- a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc +++ b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc @@ -770,8 +770,8 @@ gsi::Class decl_lefdef_config ("db", "LEFDEFReaderConfi "This property has been added in version 0.27.\n" ) + gsi::method ("macro_resolution_mode", &db::LEFDEFReaderOptions::macro_resolution_mode, - "@brief Gets the macro resolution mode.\n" - "This property describes the way LEF macros are turned into GDS cells. There " + "@brief Gets the macro resolution mode (LEF macros into DEF).\n" + "This property describes the way LEF macros are turned into layout cells when reading DEF. There " "are three modes available:\n" "\n" "@ul\n" @@ -786,7 +786,7 @@ gsi::Class decl_lefdef_config ("db", "LEFDEFReaderConfi "This property has been added in version 0.27.\n" ) + gsi::method ("macro_resolution_mode=", &db::LEFDEFReaderOptions::set_macro_resolution_mode, gsi::arg ("mode"), - "@brief Sets the macro resolution mode.\n" + "@brief Sets the macro resolution mode (LEF macros into DEF).\n" "See \\macro_resolution_mode for details about this property.\n" "\n" "This property has been added in version 0.27.\n" diff --git a/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui b/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui index 18825ffa5..73212cb25 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui +++ b/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui @@ -294,7 +294,7 @@ - LEF import + LEF import into DEF @@ -708,6 +708,8 @@ If a layer map file is given, pattern based rules are ignored. If used inside a technology, the file will be looked up relative to the technology's base path. +Otherwise it's looked up relative to the LEF or DEF file. + (2*) Die area, Blockage and Region layers in map file will have priority over global production rules above. diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index 93921cac8..f896ab0e5 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -50,16 +50,13 @@ static db::LEFDEFReaderOptions default_options () return tc; } -static db::LayerMap run_test (tl::TestBase *_this, const char *lef_dir, const char *filename, const char *au, const db::LEFDEFReaderOptions &options, bool priv = true) +static db::LayerMap read (db::Layout &layout, const char *lef_dir, const char *filename, const db::LEFDEFReaderOptions &options, bool priv = true) { std::string fn_path (priv ? tl::testsrc_private () : tl::testsrc ()); fn_path += "/testdata/lefdef/"; fn_path += lef_dir; fn_path += "/"; - db::Manager m (false); - db::Layout layout (&m), layout2 (&m), layout_au (&m); - tl::Extractor ex (filename); db::LEFDEFReaderState ld (&options, layout, fn_path); @@ -142,6 +139,16 @@ static db::LayerMap run_test (tl::TestBase *_this, const char *lef_dir, const ch ld.finish (layout); + return ld.layer_map (); +} + +static db::LayerMap run_test (tl::TestBase *_this, const char *lef_dir, const char *filename, const char *au, const db::LEFDEFReaderOptions &options, bool priv = true) +{ + db::Manager m (false); + db::Layout layout (&m), layout2 (&m), layout_au (&m); + + db::LayerMap lm = read (layout, lef_dir, filename, options, priv); + // normalize the layout by writing to OASIS and reading from .. // generate a "unique" name ... @@ -200,7 +207,7 @@ static db::LayerMap run_test (tl::TestBase *_this, const char *lef_dir, const ch } - return ld.layer_map (); + return lm; } TEST(1) @@ -549,6 +556,202 @@ TEST(115_componentmaskshift) run_test (_this, "masks-2", "lef:in_tech.lef+lef:in.lef+def:in.def", "au.oas.gz", options, false); } +TEST(116_layer_mapping) +{ + db::LEFDEFReaderOptions options = default_options (); + db::LayerMap lm = db::LayerMap::from_string_file_format ("metal1: 1\nvia1: 2\nmetal2: 3\nOUTLINE: 42/17"); + options.set_layer_map (lm); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTLINE (42/17)';'metal1.VIA : metal1 (1/0)';'metal2.VIA : metal2 (3/0)';'via1.VIA : via1 (2/0)')" + ) + } + + options.set_layer_map (db::LayerMap ()); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTLINE (4/0)';'metal1.VIA : metal1 (1/0)';'metal2.VIA : metal2 (3/0)';'via1.VIA : via1 (2/0)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("metal1: M1\nmetal1.V: M1_V\nvia1: V1\nmetal2: M2\nOUTLINE: OUTL"); + options.set_layer_map (lm); + options.set_via_geometry_suffix ("V"); + options.set_via_geometry_datatype (42); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTL (4/0)';'metal1.VIA : M1V (1/42)';'metal2.VIA : M2V (3/42)';'via1.VIA : V1V (2/42)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("metal1: M1\nmetal1.V: M1_V\nvia1: V1\nmetal2: M2"); + options.set_layer_map (lm); + options.set_via_geometry_suffix ("V"); + options.set_via_geometry_datatype (42); + options.set_read_all_layers (false); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('metal1.VIA : M1V (1/42)';'metal2.VIA : M2V (3/42)';'via1.VIA : V1V (2/42)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("metal2: M2 (17/1)"); + options.set_layer_map (lm); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('metal2.VIA : M2V (17/43)')" + ) + } + + options.set_produce_via_geometry (false); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map()" + ) + } + + options.set_produce_via_geometry (true); + options.set_via_geometry_suffix (".V"); + lm = db::LayerMap::from_string_file_format ("metal2.V: 17/1"); + options.set_layer_map (lm); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('metal2.VIA : metal2.V (17/1)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("metal2.V: m2v (17/5)"); + options.set_layer_map (lm); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('metal2.VIA : m2v (17/5)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("OUTLINE: OUTL"); + options.set_layer_map (lm); + options.set_cell_outline_layer ("OUTLINE (42/17)"); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTL (42/17)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("OUTLINE: OUTL (18/1)"); + options.set_layer_map (lm); + options.set_cell_outline_layer ("OUTLINE (42/17)"); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTL (18/1)')" + ) + } + + options.set_cell_outline_layer ("OUTLINE (42/17)"); + lm = db::LayerMap::from_string_file_format ("42/17: OUTL (18/1)"); + options.set_layer_map (lm); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTL (18/1)')" + ) + } + + options.set_cell_outline_layer ("42/17"); + lm = db::LayerMap::from_string_file_format ("42/17: 18/1"); + options.set_layer_map (lm); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTLINE (18/1)')" + ) + } +} + +TEST(117_mapfile_all) +{ + db::LEFDEFReaderOptions options = default_options (); + + db::Layout layout; + db::LayerMap lm_read = read (layout, "mapfile", "lef:in.lef+def:in.def+map:all.map", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map(" + "'OUTLINE : OUTLINE (1/0)';" + "'+M1.LEFOBS;M1.LEFPIN;M1.NET;M1.PIN;M1.SPNET;M1.VIA : \\'M1.NET/PIN/...\\' (1/5)';" + "'+M1.NET;M1.SPNET : \\'M1.NET/SPNET\\' (16/0)';" + "'+M1.NET : M1.NET (18/0)';" + "'+M1.BLK;M1.LEFOBS;M1.LEFPIN;M1.NET;M1.PIN;M1.SPNET;M1.VIA : \\'M1.NET/PIN/...\\' (22/2)';" + "'+\\'M1.NET:1\\';\\'M1.PIN:1\\';\\'M1.SPNET:1\\';\\'M1.VIA:1\\' : \\'M1.NET:1/...\\' (6/0)';" + "'+\\'M1.NET:1\\' : \\'M1.NET:1\\' (7/0)';" + "'+M1.PIN : M1.PIN (3/0)';" + "'+M1.PIN : M1.PIN (4/0)';" + "'+M1.VIA : M1.VIA (20/0)';" + "'+M1.VIA : M1.VIA (21/0)';" + "'+M1.LABEL : M1.LABEL (26/0)';" + "'+M1.LABEL : M1.LABEL (27/0)';" + "'+M1.LABEL : M1.LABEL (28/1)';" + "'+M1.BLK : M1.BLOCKAGE (13/0)';" + "'M1_TEXT.LABEL : M1_TEXT.LABEL (29/0)'" + ")" + ) +} + +TEST(118_density) +{ + run_test (_this, "density", "read:in.lef", "au.oas.gz", default_options (), false); +} + +TEST(119_multimapping) +{ + db::LEFDEFReaderOptions options = default_options (); + db::LayerMap lm = db::LayerMap::from_string_file_format ("(M1:1/0)\n(M2:3/0)\n+(M1:100/0)\n+(M2:100/0)\n(VIA1:2/0)"); + options.set_layer_map (lm); + + db::LayerMap lm_read = run_test (_this, "multimap", "def:test.def", "au.oas.gz", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map(" + "'OUTLINE : OUTLINE (4/0)';" + "'+M1.VIA : M1 (1/0)';" + "'+M1.VIA;M2.VIA : \\'M1;M2\\' (100/0)';" + "'+M2.VIA : M2 (3/0)';" + "'VIA1.VIA : VIA1 (2/0)'" + ")" + ) +} + TEST(200_lefdef_plugin) { db::Layout ly; @@ -594,3 +797,4 @@ TEST(201_lefdef_plugin_explicit_lef) db::compare_layouts (_this, ly, fn_path + "au_plugin_alt_lef.oas.gz", db::WriteOAS); } + diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc index f60be213e..29bb3fe1d 100644 --- a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc +++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc @@ -72,13 +72,9 @@ MAGReader::read (db::Layout &layout) const LayerMap & MAGReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) { - prepare_layers (); + prepare_layers (layout); - mp_klayout_tech = 0; - std::string klayout_tech_name = layout.meta_info_value ("technology"); - if (! klayout_tech_name.empty () && db::Technologies::instance ()->has_technology (klayout_tech_name)) { - mp_klayout_tech = db::Technologies::instance ()->technology_by_name (klayout_tech_name); - } + mp_klayout_tech = layout.technology (); const db::MAGReaderOptions &specific_options = options.get_options (); m_lambda = specific_options.lambda; @@ -87,9 +83,7 @@ MAGReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) m_merge = specific_options.merge; mp_current_stream = 0; - db::LayerMap lm = specific_options.layer_map; - lm.prepare (layout); - set_layer_map (lm); + set_layer_map (specific_options.layer_map); set_create_layers (specific_options.create_other_layers); set_keep_layer_names (specific_options.keep_layer_names); @@ -110,6 +104,8 @@ MAGReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) m_dbu_trans_inv = db::CplxTrans (m_dbu).inverted (); m_tech.clear (); + prepare_layers (layout); + { tl::SelfTimer timer (tl::verbosity () >= 11, "Reading MAGIC file tree"); @@ -129,7 +125,7 @@ MAGReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) } finish_layers (layout); - return layer_map (); + return layer_map_out (); } void diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGReader.h b/src/plugins/streamers/magic/db_plugin/dbMAGReader.h index 8a5fb31a6..3d9d4ecf2 100644 --- a/src/plugins/streamers/magic/db_plugin/dbMAGReader.h +++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.h @@ -145,7 +145,7 @@ private: std::map m_use_lib_paths; db::VCplxTrans m_dbu_trans_inv; std::string m_tech; - db::Technology *mp_klayout_tech; + const db::Technology *mp_klayout_tech; void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); void do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl::TextInputStream &stream); diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc index 8c54b9f01..eb4b6a743 100644 --- a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc +++ b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc @@ -122,7 +122,7 @@ MAGWriter::write_dummmy_top (const std::set &cell_set, cons std::string tech = m_options.tech; if (tech.empty ()) { - tech = layout.meta_info_value ("technology"); + tech = layout.technology_name (); } if (! tech.empty ()) { os << "tech " << make_string (tl::to_lower_case (tech)) << "\n"; @@ -177,7 +177,7 @@ MAGWriter::do_write_cell (db::cell_index_type ci, const std::vector map_layer (const db::LayerProperties &lprops) - { - // named layers that are imported from a library are ignored - if (lprops.is_named ()) { - return std::make_pair (false, 0); - } else { - return mp_reader->open_dl (*mp_layout, LDPair (lprops.layer, lprops.datatype), m_create); - } - } - -private: - db::OASISReader *mp_reader; - db::Layout *mp_layout; - bool m_create; -}; - // --------------------------------------------------------------- // OASISReader @@ -101,7 +72,6 @@ OASISReader::OASISReader (tl::InputStream &s) mm_last_property_name (this, "last-property-name"), mm_last_property_is_sprop (this, "last-property-is-stdprop"), mm_last_value_list(this, "last-value-list"), - m_create_layers (false), m_read_texts (true), m_read_properties (true), m_read_all_properties (false), @@ -129,40 +99,17 @@ OASISReader::~OASISReader () // .. nothing yet .. } -const LayerMap & -OASISReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) +void +OASISReader::init (const db::LoadLayoutOptions &options) { - db::OASISReaderOptions oasis_options = options.get_options (); - db::CommonReaderOptions common_options = options.get_options (); + CommonReader::init (options); - m_layer_map = common_options.layer_map; - m_layer_map.prepare (layout); - m_layers_created.clear (); - m_read_texts = common_options.enable_text_objects; - m_read_properties = common_options.enable_properties; - m_create_layers = common_options.create_other_layers; + m_read_texts = common_options ().enable_text_objects; + m_read_properties = common_options ().enable_properties; + + db::OASISReaderOptions oasis_options = options.get_options (); m_read_all_properties = oasis_options.read_all_properties; m_expect_strict_mode = oasis_options.expect_strict_mode; - - set_cell_conflict_resolution (common_options.cell_conflict_resolution); - - layout.start_changes (); - try { - do_read (layout); - finish (layout); - layout.end_changes (); - } catch (...) { - layout.end_changes (); - throw; - } - - return m_layer_map; -} - -const LayerMap & -OASISReader::read (db::Layout &layout) -{ - return read (layout, db::LoadLayoutOptions ()); } inline long long @@ -554,44 +501,6 @@ OASISReader::warn (const std::string &msg) } } -std::pair -OASISReader::open_dl (db::Layout &layout, const LDPair &dl, bool create) -{ - std::pair ll = m_layer_map.logical (dl, layout); - if (ll.first) { - - return ll; - - } else if (! create) { - - return ll; - - } else { - - // and create the layer - db::LayerProperties lp; - lp.layer = dl.layer; - lp.datatype = dl.datatype; - - // resolve OASIS name if possible - const tl::interval_map *names_dmap = m_layernames.mapped (dl.layer); - if (names_dmap != 0) { - const std::string *name = names_dmap->mapped (dl.datatype); - if (name != 0) { - lp.name = *name; - } - } - - unsigned int ll = layout.insert_layer (lp); - m_layer_map.map (dl, ll, lp); - - m_layers_created.insert (ll); - - return std::make_pair (true, ll); - - } -} - /** * @brief A helper class to join two datatype layer name map members */ @@ -599,12 +508,7 @@ struct LNameJoinOp1 { void operator() (std::string &a, const std::string &b) { - if (a != b) { - if (! a.empty ()) { - a += ";"; - } - a += b; - } + join_layer_names (a, b); } }; @@ -765,7 +669,6 @@ OASISReader::do_read (db::Layout &layout) m_textstrings.clear (); m_propstrings.clear (); m_propnames.clear (); - m_layernames.clear (); m_instances.clear (); m_instances_with_props.clear (); @@ -1077,21 +980,8 @@ OASISReader::do_read (db::Layout &layout) LNameJoinOp1 op1; dt_map.add (dt1, dt2 + 1, name, op1); LNameJoinOp2 op2; - m_layernames.add (l1, l2 + 1, dt_map, op2); + layer_names ().add (l1, l2 + 1, dt_map, op2); - // rename layers created before if required - for (std::set::const_iterator i = m_layers_created.begin (); i != m_layers_created.end (); ++i) { - const db::LayerProperties &lp = layout.get_properties (*i); - if (lp.layer >= l1 && lp.layer <= l2 && lp.datatype >= dt1 && lp.datatype <= dt2 && lp.name != name) { - // need to rename: add a new madding to m_layer_map and adjust the layout's layer properties - db::LayerProperties lpp = lp; - LNameJoinOp1 nj; - nj (lpp.name, name); - layout.set_properties (*i, lpp); - m_layer_map.map (LDPair (lp.layer, lp.datatype), *i, lpp); - } - } - reset_modal_variables (); // ignore properties attached to this name item @@ -2038,7 +1928,7 @@ OASISReader::do_read_text (bool xy_absolute, std::pair ll (false, 0); if (m_read_texts) { - ll = open_dl (layout, LDPair (mm_textlayer.get (), mm_texttype.get ()), m_create_layers); + ll = open_dl (layout, LDPair (mm_textlayer.get (), mm_texttype.get ())); } if ((m & 0x4) && read_repetition ()) { @@ -2180,7 +2070,7 @@ OASISReader::do_read_rectangle (bool xy_absolute, db::Box box (db::Point (mm_geometry_x.get (), mm_geometry_y.get ()), db::Point (mm_geometry_x.get () + mm_geometry_w.get (), mm_geometry_y.get () + mm_geometry_h.get ())); - std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ()), m_create_layers); + std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); if ((m & 0x4) && read_repetition ()) { @@ -2294,7 +2184,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, db::Vector pos (mm_geometry_x.get (), mm_geometry_y.get ()); - std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ()), m_create_layers); + std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); if ((m & 0x4) && read_repetition ()) { @@ -2461,7 +2351,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db: db::Vector pos (mm_geometry_x.get (), mm_geometry_y.get ()); - std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ()), m_create_layers); + std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); if ((m & 0x4) && read_repetition ()) { @@ -2620,7 +2510,7 @@ OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index db::Vector pos (mm_geometry_x.get (), mm_geometry_y.get ()); - std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ()), m_create_layers); + std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); db::Point pts [4]; @@ -2775,7 +2665,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index db::Vector pos (mm_geometry_x.get (), mm_geometry_y.get ()); - std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ()), m_create_layers); + std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); db::Point pts [4]; @@ -3126,7 +3016,7 @@ OASISReader::do_read_circle (bool xy_absolute, db::cell_index_type cell_index, d db::Vector pos (mm_geometry_x.get (), mm_geometry_y.get ()); - std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ()), m_create_layers); + std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); // ignore this circle if the radius is zero if (mm_circle_radius.get () <= 0) { @@ -3444,7 +3334,7 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) // Restore proxy cell (link to PCell or Library) if (has_context) { - OASISReaderLayerMapping layer_mapping (this, &layout, m_create_layers); + CommonReaderLayerMapping layer_mapping (this, &layout); layout.recover_proxy_as (cell_index, context_strings.begin (), context_strings.end (), &layer_mapping); } diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h index a8a56e173..89b18d5fd 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h @@ -81,39 +81,6 @@ public: */ ~OASISReader (); - /** - * @brief The basic read method - * - * This method will read the stream data and translate this to - * insert calls into the layout object. This will not do much - * on the layout object beside inserting the objects. - * A set of options can be specified with the LoadLayoutOptions - * object. - * The returned map will contain all layers, the passed - * ones and the newly created ones. - * - * @param layout The layout object to write to - * @param map The LayerMap object - * @param create true, if new layers should be created - * @return The LayerMap object that tells where which layer was loaded - */ - virtual const LayerMap &read (db::Layout &layout, const LoadLayoutOptions &options); - - /** - * @brief The basic read method (without mapping) - * - * This method will read the stream data and translate this to - * insert calls into the layout object. This will not do much - * on the layout object beside inserting the objects. - * This version will read all input layers and return a map - * which tells which OASIS layer has been read into which logical - * layer. - * - * @param layout The layout object to write to - * @return The LayerMap object - */ - virtual const LayerMap &read (db::Layout &layout); - /** * @brief Format */ @@ -136,6 +103,8 @@ protected: virtual void common_reader_error (const std::string &msg) { error (msg); } virtual void common_reader_warn (const std::string &msg) { warn (msg); } + virtual void init (const LoadLayoutOptions &options); + virtual void do_read (db::Layout &layout); private: friend class OASISReaderLayerMapping; @@ -153,8 +122,6 @@ private: }; tl::InputStream &m_stream; - LayerMap m_layer_map; - std::set m_layers_created; tl::AbsoluteProgress m_progress; std::string m_cellname; double m_dbu; @@ -204,12 +171,10 @@ private: std::map m_text_forward_references; std::map m_propstrings; std::map m_propnames; - tl::interval_map > m_layernames; tl::vector m_instances; tl::vector m_instances_with_props; - bool m_create_layers; bool m_read_texts; bool m_read_properties; bool m_read_all_properties; @@ -219,7 +184,6 @@ private: db::property_names_id_type m_s_gds_property_name_id; db::property_names_id_type m_klayout_context_property_name_id; - void do_read (db::Layout &layout); void do_read_cell (db::cell_index_type cell_index, db::Layout &layout); void do_read_placement (unsigned char r, @@ -310,8 +274,6 @@ private: db::Coord get_coord (long grid = 1); db::Coord get_ucoord (unsigned long grid = 1); distance_type get_ucoord_as_distance (unsigned long grid = 1); - - std::pair open_dl (db::Layout &layout, const LDPair &dl, bool create); }; } diff --git a/testdata/gds/alm_au2.gds b/testdata/gds/alm_au2.gds new file mode 100644 index 000000000..c1267e0bd Binary files /dev/null and b/testdata/gds/alm_au2.gds differ diff --git a/testdata/gds/alm_au3.gds b/testdata/gds/alm_au3.gds new file mode 100644 index 000000000..324c893f7 Binary files /dev/null and b/testdata/gds/alm_au3.gds differ diff --git a/testdata/lefdef/density/au.oas.gz b/testdata/lefdef/density/au.oas.gz new file mode 100644 index 000000000..c24d3412b Binary files /dev/null and b/testdata/lefdef/density/au.oas.gz differ diff --git a/testdata/lefdef/density/in.lef b/testdata/lefdef/density/in.lef new file mode 100644 index 000000000..abc0ccbf6 --- /dev/null +++ b/testdata/lefdef/density/in.lef @@ -0,0 +1,28 @@ +LAYER M1 + TYPE ROUTING ; +END M1 +LAYER VIA1 + TYPE CUT ; +END VIA1 + +MACRO dense + CLASS CORE ; + ORIGIN 0.0 0.0 ; + FOREIGN dense ; + SIZE 0.7 BY 0.9 ; + OBS + LAYER M1 DESIGNRULEWIDTH 0.05 ; + RECT 0.04 0.095 0.66 0.685 ; + LAYER VIA1 ; + RECT 0.285 0.31 0.415 0.36 ; + RECT 0.285 0.18 0.415 0.23 ; + END + DENSITY + LAYER M1 ; + RECT 0.04 0.09 0.66 0.685 100.0 ; + LAYER VIA1 ; + RECT 0 0 100 100 45.5 ; #rect from (0,0) to (100,100), density of 45.5% + RECT 100 0 200 100 42.2 ; #rect from (100,0) to (200, 100), density of 42.2% + END +END dense +END LIBRARY diff --git a/testdata/lefdef/mapfile/all.map b/testdata/lefdef/mapfile/all.map new file mode 100644 index 000000000..462c62ea3 --- /dev/null +++ b/testdata/lefdef/mapfile/all.map @@ -0,0 +1,30 @@ +# some variations of map file entries +DIEAREA ALL 1 0 +COMP ALL 2 0 +M1 PIN 3,4 0 +M1 LEFPIN,LEFOBS,PIN,NET,SPNET,VIA 1 5 +DOES NOT MEAN ANYTHING +M1 NET,SPNET,PIN,VIA:MASK:1 6 0 +M1 NET:MASK:1 7 0 +M1 TEXT 8 0 +M1 FILLOPC 9 0 +M1 FILLOPC:MASK:1 10 0 +M1 FILLOPC:MASK:2 11 0 +M1 BLOCKAGEFILL 12 0 +M1 BLOCKAGE 13 0 +M1 FILL 14 0 +M1 VIAFILL,FILL 15 0 +M1 NET,SPNET,CUSTOM 16 0 +M1 VIAFILL:FLOATING,FILL:FLOATING 17 0 +M1 NET:VOLTAGE:0.8 18 0 +M1 VIAFILL 19 0 +M1 VIA:SIZE:0.05x0.05 20 0 +M1 VIA:SIZE:3x3 21 0 +M1 ALL 22 2 +NAME DIEAREA 23 0 +NAME ALL 24 0 +NAME COMP 25 0 +NAME M1/PIN 26 0 +NAME M1/NET 27 0 +NAME M1/SPNET 28 1 +NAME M1_TEXT 29 0 diff --git a/testdata/lefdef/multimap/au.oas.gz b/testdata/lefdef/multimap/au.oas.gz new file mode 100644 index 000000000..e1bb4bd44 Binary files /dev/null and b/testdata/lefdef/multimap/au.oas.gz differ diff --git a/testdata/lefdef/multimap/test.def b/testdata/lefdef/multimap/test.def new file mode 100644 index 000000000..6381b8523 --- /dev/null +++ b/testdata/lefdef/multimap/test.def @@ -0,0 +1,28 @@ + +VERSION 5.8 ; +DIVIDERCHAR "/" ; +BUSBITCHARS "[]" ; +DESIGN chip_top ; +UNITS DISTANCE MICRONS 1000 ; +DIEAREA ( 30000 3000 ) ( 10000000 4000 ) ; +VIAS 1 ; + - VIA1_dummy + + RECT M1 ( -200 -140 ) ( 200 140 ) + + RECT VIA1 ( -100 -100 ) ( 100 100 ) + + RECT M2 ( -300 -120 ) ( 300 120 ) ; +END VIAS +SPECIALNETS 1 ; +- dummy + + ROUTED M1 150 + SHAPE IOWIRE ( 40000 3600 ) VIA1_dummy DO 16000 BY 1 STEP 700 0 +; +END SPECIALNETS +SCANCHAINS 77 ; +- chain1_clock1 ++ PARTITION clock1 ++ START block1/bsr_reg_0 Q ++ FLOATING +block1/pgm_cgm_en_reg_reg ( IN SD ) ( OUT QZ ) +block1/start_reset_dd_reg ( IN SD ) ( OUT QZ ) ++ STOP block1/start_reset_d_reg SD ; +END SCANCHAINS +END DESIGN diff --git a/testdata/ruby/dbLayout.rb b/testdata/ruby/dbLayout.rb index e5ff45791..c7ec7732e 100644 --- a/testdata/ruby/dbLayout.rb +++ b/testdata/ruby/dbLayout.rb @@ -30,7 +30,11 @@ class DBLayout_TestClass < TestBase lmap = RBA::LayerMap::new lmap.map( "1/0", 0 ) + assert_equal(lmap.is_mapped(RBA::LayerInfo::new(1, 0)), true) + assert_equal(lmap.is_mapped(RBA::LayerInfo::new(1, 1)), false) + assert_equal(lmap.is_mapped(RBA::LayerInfo::new(2, 2)), false) lmap.map( "2/2", 0 ) + assert_equal(lmap.is_mapped(RBA::LayerInfo::new(2, 2)), true) lmap.map( "10/2", 0 ) assert_equal( lmap.mapping_str(0), "1/0;2/2;10/2" ) @@ -39,7 +43,7 @@ class DBLayout_TestClass < TestBase lmap.map( "4/2", 1 ) lmap.map( "1/2", 1 ) lmap.map( "0/0", 1 ) - assert_equal( lmap.mapping_str(1), "0/0;1/2;2-4/2" ) # could be "0/0;1-4/2" as well ... + assert_equal( lmap.mapping_str(1), "0/0;1-4/2" ) # could be "0/0;1-4/2" as well ... lmap.map( RBA::LayerInfo::new(2, 2), RBA::LayerInfo::new(4, 4), 2 ) lmap.map( RBA::LayerInfo::new(0, 1), 2 ) @@ -85,6 +89,55 @@ class DBLayout_TestClass < TestBase assert_equal( lmap.mapping_str(0), "2/2 : 4/4" ) assert_equal( lmap.mapping(0).to_s, "4/4" ) assert_equal( lmap.mapping(2).to_s, "5/5" ) + + lmap = RBA::LayerMap::new + + lmap.map("*/*", 0) + lmap.unmap(RBA::LayerInfo::new(5, 10)) + assert_equal(lmap.mapping_str(0), "0-4/*;5/0-9,11-*;6-*/*") + + lmap.clear + lmap.map("*/*", 0) + lmap.unmap(RBA::LayerInfo::new(5, 10), RBA::LayerInfo::new(16, 21)) + assert_equal(lmap.mapping_str(0), "0-4/*;5-16/0-9,22-*;17-*/*") + + lmap.clear + lmap.map("*/*", 0) + lmap.unmap("5-16/10-21") + assert_equal(lmap.mapping_str(0), "0-4/*;5-16/0-9,22-*;17-*/*") + + lmap.clear + lmap.map("*/*", 0) + lmap.mmap(RBA::LayerInfo::new(5, 10), 1) + assert_equal(lmap.mapping_str(0), "+*/*") + assert_equal(lmap.mapping_str(1), "+5/10") + + lmap.clear + lmap.map("*/*", 0) + lmap.mmap(RBA::LayerInfo::new(5, 10), 1, RBA::LayerInfo::new(100, 0)) + assert_equal(lmap.mapping_str(0), "+*/*") + assert_equal(lmap.mapping_str(1), "+5/10 : 100/0") + + lmap.clear + lmap.map("*/*", 0) + lmap.mmap(RBA::LayerInfo::new(5, 10), RBA::LayerInfo::new(16, 21), 1) + assert_equal(lmap.mapping_str(0), "+*/*") + assert_equal(lmap.mapping_str(1), "+5-16/10-21") + + lmap.clear + lmap.map("*/*", 0) + lmap.mmap(RBA::LayerInfo::new(5, 10), RBA::LayerInfo::new(16, 21), 1, RBA::LayerInfo::new(100, 0)) + assert_equal(lmap.mapping_str(0), "+*/*") + assert_equal(lmap.mapping_str(1), "+5-16/10-21 : 100/0") + + lmap.clear + lmap.map("*/*", 0) + lmap.mmap("5-16/10-21", 1) + assert_equal(lmap.mapping_str(0), "+*/*") + assert_equal(lmap.mapping_str(1), "+5-16/10-21") + + assert_equal(lmap.logicals(RBA::LayerInfo::new(5, 10)), [ 0, 1 ]) + assert_equal(lmap.logicals(RBA::LayerInfo::new(0, 10)), [ 0 ]) end