diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc index 0b1522e82..4a1adf813 100644 --- a/src/db/db/dbCell.cc +++ b/src/db/db/dbCell.cc @@ -506,7 +506,7 @@ Cell::collect_caller_cells (std::set &callers, const std::setis_valid_cell_index (*cc)) { callers.insert (*cc); mp_layout->cell (*cc).collect_caller_cells (callers, levels < 0 ? levels : levels - 1); } @@ -519,7 +519,7 @@ Cell::collect_caller_cells (std::set &callers, int levels) cons { if (levels != 0) { for (parent_cell_iterator cc = begin_parent_cells (); cc != end_parent_cells (); ++cc) { - if (callers.find (*cc) == callers.end ()) { + if (callers.find (*cc) == callers.end () && mp_layout->is_valid_cell_index (*cc)) { callers.insert (*cc); mp_layout->cell (*cc).collect_caller_cells (callers, levels < 0 ? levels : levels - 1); } @@ -538,7 +538,7 @@ Cell::collect_called_cells (std::set &called, int levels) const { if (levels != 0) { for (child_cell_iterator cc = begin_child_cells (); ! cc.at_end (); ++cc) { - if (called.find (*cc) == called.end ()) { + if (called.find (*cc) == called.end () && mp_layout->is_valid_cell_index (*cc)) { called.insert (*cc); mp_layout->cell (*cc).collect_called_cells (called, levels < 0 ? levels : levels - 1); } diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index 9802aedb9..2d4afc86d 100644 --- a/src/db/db/dbCommonReader.cc +++ b/src/db/db/dbCommonReader.cc @@ -29,6 +29,361 @@ namespace db { +// --------------------------------------------------------------- +// Common reader implementation + +static const size_t null_id = std::numeric_limits::max (); + +CommonReader::CommonReader () + : m_cc_resolution (AddToCell) +{ + // .. nothing yet .. +} + +db::cell_index_type +CommonReader::make_cell (db::Layout &layout, const std::string &cn) +{ + tl_assert (! cn.empty ()); + + std::map >::iterator iname = m_name_map.find (cn); + if (iname != m_name_map.end ()) { + + db::Cell &cell = layout.cell (iname->second.second); + + if (! cell.is_ghost_cell ()) { + common_reader_error (tl::sprintf (tl::to_string (tr ("A cell with name %s already exists")), cn)); + } + + cell.set_ghost_cell (false); + return cell.cell_index (); + + } else { + + db::cell_index_type ci = layout.add_anonymous_cell (); + + m_name_map [cn] = std::make_pair (null_id, ci); + return ci; + + } +} + +bool +CommonReader::has_cell (const std::string &cn) const +{ + return m_name_map.find (cn) != m_name_map.end (); +} + +std::pair +CommonReader::cell_by_name (const std::string &cn) const +{ + std::map >::const_iterator iname = m_name_map.find (cn); + if (iname != m_name_map.end ()) { + return std::make_pair (true, iname->second.second); + } else { + return std::make_pair (false, size_t (0)); + } +} + +db::cell_index_type +CommonReader::make_cell (db::Layout &layout, size_t id) +{ + tl_assert (id != null_id); + + std::map >::iterator iid = m_id_map.find (id); + if (iid != m_id_map.end ()) { + + db::Cell &cell = layout.cell (iid->second.second); + + if (! cell.is_ghost_cell ()) { + common_reader_error (tl::sprintf (tl::to_string (tr ("A cell with ID %ld already exists")), id)); + } + + cell.set_ghost_cell (false); + return cell.cell_index (); + + } else { + + db::cell_index_type ci = layout.add_anonymous_cell (); + + m_id_map [id] = std::make_pair (std::string (), ci); + return ci; + + } +} + +bool +CommonReader::has_cell (size_t id) const +{ + return m_id_map.find (id) != m_id_map.end (); +} + +std::pair +CommonReader::cell_by_id (size_t id) const +{ + std::map >::const_iterator iid = m_id_map.find (id); + if (iid != m_id_map.end ()) { + return std::make_pair (true, iid->second.second); + } else { + return std::make_pair (false, size_t (0)); + } +} + +const std::string & +CommonReader::name_for_id (size_t id) const +{ + std::map::const_iterator n = m_name_for_id.find (id); + if (n != m_name_for_id.end ()) { + return n->second; + } else { + static std::string empty; + return empty; + } +} + +void +CommonReader::rename_cell (db::Layout &layout, size_t id, const std::string &cn) +{ + m_name_for_id.insert (std::make_pair (id, cn)); + + std::map >::iterator iid = m_id_map.find (id); + std::map >::iterator iname = m_name_map.find (cn); + + if (iid != m_id_map.end () && iname != m_name_map.end ()) { + + if (! iid->second.first.empty () && iid->second.first != cn) { + common_reader_error (tl::sprintf (tl::to_string (tr ("Cell named %s with ID %ld was already given name %s")), cn, id, iid->second.first)); + } + + if (iname->second.second != iid->second.second) { + + // Both cells already exist and are not identical: merge ID-declared cell into the name-declared one + layout.force_update (); + merge_cell (layout, iname->second.second, iid->second.second); + iid->second.second = iname->second.second; + + } + + iid->second.first = cn; + iname->second.first = id; + + } else if (iid != m_id_map.end ()) { + + m_name_map [cn] = std::make_pair (id, iid->second.second); + iid->second.first = cn; + + } else if (iname != m_name_map.end ()) { + + m_id_map [id] = std::make_pair (cn, iname->second.second); + iname->second.first = id; + + } else { + + db::cell_index_type ci = layout.add_anonymous_cell (); + layout.cell (ci).set_ghost_cell (true); + + m_id_map [id] = std::make_pair (cn, ci); + m_name_map [cn] = std::make_pair (id, ci); + + } +} + +db::cell_index_type +CommonReader::cell_for_instance (db::Layout &layout, size_t id) +{ + tl_assert (id != null_id); + + std::map >::iterator iid = m_id_map.find (id); + if (iid != m_id_map.end ()) { + + return iid->second.second; + + } else { + + db::cell_index_type ci = layout.add_anonymous_cell (); + layout.cell (ci).set_ghost_cell (true); + + m_id_map [id] = std::make_pair (std::string (), ci); + return ci; + + } +} + +db::cell_index_type +CommonReader::cell_for_instance (db::Layout &layout, const std::string &cn) +{ + tl_assert (! cn.empty ()); + + std::map >::iterator iname = m_name_map.find (cn); + if (iname != m_name_map.end ()) { + + return iname->second.second; + + } else { + + db::cell_index_type ci = layout.add_anonymous_cell (); + layout.cell (ci).set_ghost_cell (true); + + m_name_map [cn] = std::make_pair (null_id, ci); + return ci; + + } +} + +void +CommonReader::merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const +{ + const db::Cell &src_cell = layout.cell (src_cell_index); + db::Cell &target_cell = layout.cell (target_cell_index); + + // copy over the instances + for (db::Cell::const_iterator i = src_cell.begin (); ! i.at_end (); ++i) { + // NOTE: cell indexed may be invalid because we delete subcells without update() + if (layout.is_valid_cell_index (i->cell_index ())) { + target_cell.insert (*i); + } + } + + merge_cell_without_instances (layout, target_cell_index, src_cell_index); +} + +void +CommonReader::merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const +{ + const db::Cell &src_cell = layout.cell (src_cell_index); + db::Cell &target_cell = layout.cell (target_cell_index); + + // copy over the shapes + for (unsigned int l = 0; l < layout.layers (); ++l) { + if (layout.is_valid_layer (l) && ! src_cell.shapes (l).empty ()) { + target_cell.shapes (l).insert (src_cell.shapes (l)); + } + } + + // replace all instances of the new cell with the original one + std::vector > parents; + for (db::Cell::parent_inst_iterator pi = src_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { + parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ())); + } + + for (std::vector >::const_iterator p = parents.begin (); p != parents.end (); ++p) { + db::CellInstArray ia = p->second.cell_inst (); + ia.object ().cell_index (target_cell.cell_index ()); + layout.cell (p->first).replace (p->second, ia); + } + + // finally delete the new cell + layout.delete_cell (src_cell.cell_index ()); +} + +void +CommonReader::finish (db::Layout &layout) +{ + bool any_missing = false; + + for (std::map >::const_iterator i = m_id_map.begin (); i != m_id_map.end (); ++i) { + if (i->second.first.empty ()) { + common_reader_warn (tl::sprintf (tl::to_string (tr ("No cellname defined for cell name id %ld")), i->first)); + any_missing = true; + } + } + + if (any_missing) { + common_reader_error (tl::to_string (tr ("Some cell IDs don't have a name (see previous warnings)"))); + } + + // check if we need to resolve conflicts + + bool has_conflict = false; + for (std::map >::const_iterator i = m_name_map.begin (); i != m_name_map.end () && ! has_conflict; ++i) { + has_conflict = layout.cell_by_name (i->first.c_str ()).first; + } + + if (! has_conflict) { + + // no conflict - plain rename + + for (std::map >::const_iterator i = m_name_map.begin (); i != m_name_map.end () && ! has_conflict; ++i) { + layout.rename_cell (i->second.second, i->first.c_str ()); + } + + } else { + + // elaborate conflict resolution + + layout.force_update (); + + std::map new_cells; + for (std::map >::const_iterator i = m_name_map.begin (); i != m_name_map.end (); ++i) { + new_cells.insert (std::make_pair (i->second.second, i->first)); + } + + std::vector > cells_with_conflict; + + // First treat all the cells without conflict + for (db::Layout::bottom_up_iterator bu = layout.begin_bottom_up (); bu != layout.end_bottom_up (); ++bu) { + + db::cell_index_type ci_new = *bu; + std::map::const_iterator i = new_cells.find (ci_new); + + if (i == new_cells.end ()) { + // not a new cell + continue; + } + + std::pair c2n = layout.cell_by_name (i->second.c_str ()); + db::cell_index_type ci_org = c2n.second; + + // NOTE: proxy cells are never resolved. "RenameCell" is a plain and simple case. + if (c2n.first && m_cc_resolution != RenameCell && ! layout.cell (ci_org).is_proxy ()) { + cells_with_conflict.push_back (std::make_pair (ci_new, ci_org)); + } else { + layout.rename_cell (ci_new, layout.uniquify_cell_name (i->second.c_str ()).c_str ()); + } + + } + + // Then treat all the cells with conflict + for (std::vector >::const_iterator cc = cells_with_conflict.begin (); cc != cells_with_conflict.end (); ++cc) { + + db::cell_index_type ci_new = cc->first; + db::cell_index_type ci_org = cc->second; + + // we have a cell conflict + + if (m_cc_resolution == OverwriteCell && ! layout.cell (ci_new).is_ghost_cell ()) { + + if (! layout.cell (ci_org).begin ().at_end ()) { + + // NOTE: because prune_subcells needs the parents for sub cells and we are going do delete + // the current cell, we cannot save the "update()" just by traversing bottom-up. + layout.force_update (); + layout.prune_subcells (ci_org); + + } + + layout.cell (ci_org).clear_shapes (); + + merge_cell (layout, ci_org, ci_new); + + } else if (m_cc_resolution == SkipNewCell && ! layout.cell (ci_org).is_ghost_cell ()) { + + layout.prune_subcells (ci_new); + layout.cell (ci_new).clear_shapes (); + + // NOTE: ignore instances -> this saves us a layout update + merge_cell_without_instances (layout, ci_org, ci_new); + + } else { + + merge_cell (layout, ci_org, ci_new); + + } + + } + + } +} + // --------------------------------------------------------------- // Common format declaration diff --git a/src/db/db/dbCommonReader.h b/src/db/db/dbCommonReader.h index 45c09861d..f0ed56bcf 100644 --- a/src/db/db/dbCommonReader.h +++ b/src/db/db/dbCommonReader.h @@ -31,6 +31,128 @@ namespace db { +/** + * @brief A common reader base for GDS2 and OASIS providing common services for both readers + */ +class DB_PUBLIC CommonReader + : public ReaderBase +{ +public: + /** + * @brief The CellConflictResolution enum + */ + enum CellConflictResolution + { + AddToCell = 0, + OverwriteCell = 1, + SkipNewCell = 2, + RenameCell = 3 + }; + + /** + * @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 + */ + db::cell_index_type make_cell (db::Layout &layout, const std::string &cn); + + /** + * @brief Returns true, if there is a cell with the given name already + */ + bool has_cell (const std::string &cn) const; + + /** + * @brief Returns a pair with a bool (indicating whether the cell name is known) and the cell index for this name + */ + std::pair cell_by_name (const std::string &name) const; + + /** + * @brief Make a cell from an ID (OASIS) + */ + db::cell_index_type make_cell (db::Layout &layout, size_t id); + + /** + * @brief Returns true, if there is a cell with the given ID alreay + */ + bool has_cell (size_t id) const; + + /** + * @brief Returns a pair with a bool (indicating whether the cell ID is known) and the cell index for this ID + */ + std::pair cell_by_id (size_t id) const; + + /** + * @brief Registers a cell name for an ID + */ + void rename_cell (db::Layout &layout, size_t id, const std::string &cn); + + /** + * @brief Gets the name for a given cell ID if known, otherwise returns an empty string + */ + const std::string &name_for_id (size_t id) const; + + /** + * @brief Returns a cell reference by ID + * If the cell does not exist, it's created. It is marked as ghost cell until + * "make_cell" is called. + */ + db::cell_index_type cell_for_instance (db::Layout &layout, size_t id); + + /** + * @brief Returns a cell reference by name + * Same as the previous method, but acting on cell names. + */ + db::cell_index_type cell_for_instance (db::Layout &layout, const std::string &cn); + + /** + * @brief Finishes the reading process + * + * This method will first check if all cells IDs got a name. + * After this, the cells are renamed and cell conflict resolution will happen in the + * specified way (cell_conflict_resolution attribute). + */ + void finish (db::Layout &layout); + +protected: + virtual void common_reader_error (const std::string &msg) = 0; + virtual void common_reader_warn (const std::string &msg) = 0; + + /** + * @brief Merge (and delete) the src_cell into target_cell + */ + void merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const; + + /** + * @brief Merge (and delete) the src_cell into target_cell without instances + */ + void merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const; + +private: + std::map > m_id_map; + std::map > m_name_map; + std::map m_name_for_id; + CellConflictResolution m_cc_resolution; +}; + /** * @brief Structure that holds the GDS2 and OASIS specific options for the reader */ @@ -44,7 +166,8 @@ public: CommonReaderOptions () : create_other_layers (true), enable_text_objects (true), - enable_properties (true) + enable_properties (true), + cell_conflict_resolution (CommonReader::AddToCell) { // .. nothing yet .. } @@ -83,6 +206,22 @@ public: */ 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 */ diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 55c19ef78..199441231 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1068,14 +1068,41 @@ Layout::add_cell (const char *name) return new_index; } +cell_index_type +Layout::add_anonymous_cell () +{ + std::string b; + + // create a new cell + cell_index_type new_index = allocate_new_cell (); + + cell_type *new_cell = new cell_type (new_index, *this); + m_cells.push_back_ptr (new_cell); + m_cell_ptrs [new_index] = new_cell; + + // enter it's index and cell_name + register_cell_name (0, 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::register_cell_name (const char *name, cell_index_type ci) { // enter it's index and cell_name char *cp; - cp = new char [strlen (name) + 1]; - strcpy (cp, name); + if (name == 0) { + cp = new char [1]; + *cp = 0; + } else { + cp = new char [strlen (name) + 1]; + strcpy (cp, name); + } while (m_cell_names.size () < ci) { char *e = new char [1]; @@ -1090,7 +1117,9 @@ Layout::register_cell_name (const char *name, cell_index_type ci) m_cell_names.push_back (cp); } - m_cell_map.insert (std::make_pair (cp, ci)); + if (name) { + m_cell_map.insert (std::make_pair (cp, ci)); + } } void diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 827dee2b3..fd6d897aa 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -727,7 +727,17 @@ public: */ cell_index_type add_cell (const char *name = 0); - /** + /** + * @brief Add a cell without a name + * + * The cell is created, but cannot be found by name. The name returned is an empty string. + * The cell is created with the purpose of being renamed later. + * + * @return The index of the new cell + */ + cell_index_type add_anonymous_cell (); + + /** * @brief Rename a cell * * Rename the cell with the given id. @@ -1065,8 +1075,7 @@ public: * @brief Delete the subcells of the given cells which are not used otherwise * * All subcells referenced directy or indirectly but not used otherwise - * are deleted as well. This basically prunes the cell tree by this cell. - * All instances of this cell are deleted as well. + * are deleted as well. * This method is more efficent than calling prune_subcells for single cells multiple times. * * @param from A begin iterator delivering the cell id's to delete @@ -1085,8 +1094,7 @@ public: * @brief Delete the subcells of the given cells which are not used otherwise * * All subcells referenced directy or indirectly but not used otherwise - * are deleted as well. This basically prunes the cell tree by this cell. - * All instances of this cell are deleted as well. + * are deleted as well. * This method is more efficent than calling prune_subcells for single cells multiple times. * * @param cells A set of cell id's to prune diff --git a/src/db/db/dbShapes.cc b/src/db/db/dbShapes.cc index aef79fa21..dedd81cb2 100644 --- a/src/db/db/dbShapes.cc +++ b/src/db/db/dbShapes.cc @@ -174,9 +174,15 @@ Shapes::do_insert (const Shapes &d) if (layout () == d.layout ()) { // both shape containers reside in the same repository space - simply copy - m_layers.reserve (d.m_layers.size ()); - for (tl::vector::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { - m_layers.push_back ((*l)->clone (this, manager ())); + if (m_layers.empty ()) { + m_layers.reserve (d.m_layers.size ()); + for (tl::vector::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { + m_layers.push_back ((*l)->clone (this, manager ())); + } + } else { + for (tl::vector::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { + (*l)->insert_into (this); + } } } else if (layout () == 0) { diff --git a/src/db/db/dbShapes.h b/src/db/db/dbShapes.h index 1942906a0..6f7dcbb20 100644 --- a/src/db/db/dbShapes.h +++ b/src/db/db/dbShapes.h @@ -493,6 +493,7 @@ public: virtual void transform_into (Shapes *target, const Trans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const = 0; virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep) const = 0; virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const = 0; + virtual void insert_into (Shapes *target) = 0; virtual void deref_into (Shapes *target) = 0; virtual void deref_into (Shapes *target, pm_delegate_type &pm) = 0; virtual void deref_and_transform_into (Shapes *target, const Trans &trans) = 0; diff --git a/src/db/db/dbShapes2.cc b/src/db/db/dbShapes2.cc index 73fd33aad..19396588e 100644 --- a/src/db/db/dbShapes2.cc +++ b/src/db/db/dbShapes2.cc @@ -834,6 +834,13 @@ layer_class::transform_into (Shapes *target, const ICplxTrans &tr } } +template +void +layer_class::insert_into (Shapes *target) +{ + target->insert (m_layer.begin (), m_layer.end ()); +} + template void layer_class::deref_into (Shapes *target) @@ -870,6 +877,7 @@ layer_class::deref_and_transform_into (Shapes *target, const Tran { deref_and_transform_into_shapes deref_op (target); for (typename layer_type::iterator s = m_layer.begin (); s != m_layer.end (); ++s) { + deref_op (*s, trans, pm); } } diff --git a/src/db/db/dbShapes2.h b/src/db/db/dbShapes2.h index d60678e28..7c7d3ba60 100644 --- a/src/db/db/dbShapes2.h +++ b/src/db/db/dbShapes2.h @@ -149,6 +149,7 @@ public: virtual void transform_into (Shapes *target, const Trans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const; virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep) const; virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const; + virtual void insert_into (Shapes *target); virtual void deref_into (Shapes *target); virtual void deref_into (Shapes *target, pm_delegate_type &pm); virtual void deref_and_transform_into (Shapes *target, const Trans &trans); diff --git a/src/db/db/gsiDeclDbCommonStreamOptions.cc b/src/db/db/gsiDeclDbCommonStreamOptions.cc index ac2fb3223..32b8ede8c 100644 --- a/src/db/db/gsiDeclDbCommonStreamOptions.cc +++ b/src/db/db/gsiDeclDbCommonStreamOptions.cc @@ -25,6 +25,7 @@ #include "dbCommonReader.h" #include "dbLoadLayoutOptions.h" #include "gsiDecl.h" +#include "gsiEnums.h" namespace dn { @@ -84,6 +85,16 @@ 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) +{ + return options->get_options ().cell_conflict_resolution; +} + +static void set_cell_conflict_resolution (db::LoadLayoutOptions *options, db::CommonReader::CellConflictResolution cc) +{ + options->get_options ().cell_conflict_resolution = cc; +} + // extend lay::LoadLayoutOptions with the Common options static gsi::ClassExt common_reader_options ( @@ -156,10 +167,56 @@ gsi::ClassExt common_reader_options ( "@param enabled True, if properties should be read." "\n" "Starting with version 0.25 this option only applies to GDS2 and OASIS format. Other formats provide their own configuration." + ) + + gsi::method_ext ("cell_conflict_resolution", &get_cell_conflict_resolution, + "@brief Gets the cell conflict resolution mode\n" + "\n" + "Multiple layout files can be collected into a single Layout object by reading file after file into the Layout object. " + "Cells with same names are considered a conflict. This mode indicates how such conflicts are resolved. See \\LoadLayoutOptions::CellConflictResolution " + "for the values allowed. The default mode is \\LoadLayoutOptions::CellConflictResolution#AddToCell.\n" + "\n" + "This option has been introduced in version 0.27." + ) + + gsi::method_ext ("cell_conflict_resolution=", &set_cell_conflict_resolution, gsi::arg ("mode"), + "@brief Sets the cell conflict resolution mode\n" + "\n" + "See \\cell_conflict_resolution for details about this option.\n" + "\n" + "This option has been introduced in version 0.27." ), "" ); + +gsi::EnumIn decl_dbCommonReader_CellConflictResolution ("db", "CellConflictResolution", + gsi::enum_const ("AddToCell", db::CommonReader::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, + "@brief The old cell is overwritten entirely (including child cells which are not used otherwise)\n" + ) + + gsi::enum_const ("SkipNewCell", db::CommonReader::SkipNewCell, + "@brief The new cell is skipped entirely (including child cells which are not used otherwise)\n" + ) + + gsi::enum_const ("RenameCell", db::CommonReader::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. " + "Until version 0.26.8 and before, the mode was always 'AddToCell'. On reading, a cell was 'reopened' when encountering a cell name " + "which already existed. This mode is still the default. The other modes are made available to support other ways of merging layouts.\n" + "\n" + "Proxy cells are never modified in the existing layout. Proxy cells are always local to their layout file. So if the existing cell is " + "a proxy cell, the new cell will be renamed.\n" + "\n" + "If the new or existing cell is a ghost cell, both cells are merged always.\n" + "\n" + "This enum was introduced in version 0.27.\n" +); + +// Inject the NetlistCrossReference::Status declarations into NetlistCrossReference: +gsi::ClassExt inject_CellConflictResolution_in_parent (decl_dbCommonReader_CellConflictResolution.defs ()); + } diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index de0508386..32a6b40b6 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -460,8 +460,8 @@ TEST(BasicAnd9) // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 0 insts, 0 shapes (1 times)\n" "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" ); } @@ -476,8 +476,8 @@ TEST(BasicNot9) // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 0 insts, 0 shapes (1 times)\n" "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" ); } @@ -600,8 +600,8 @@ TEST(BasicAndWithSize9) // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 0 insts, 0 shapes (1 times)\n" "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" ); } @@ -616,8 +616,8 @@ TEST(BasicNotWithSize9) // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 0 insts, 0 shapes (1 times)\n" "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + "RING[1] 0 insts, 0 shapes (1 times)\n" ); } @@ -752,8 +752,8 @@ TEST(TwoInputsAnd9) // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 1 insts, 0 shapes (1 times)\n" "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" ); } @@ -768,8 +768,8 @@ TEST(TwoInputsNot9) // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 1 insts, 0 shapes (1 times)\n" "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" ); } @@ -892,8 +892,8 @@ TEST(TwoInputsAndWithSize9) // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 1 insts, 0 shapes (1 times)\n" "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" ); } @@ -908,8 +908,8 @@ TEST(TwoInputsNotWithSize9) // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 1 insts, 0 shapes (1 times)\n" "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + "RING[1] 1 insts, 0 shapes (1 times)\n" ); } @@ -984,8 +984,8 @@ TEST(BasicSelfOverlap9) // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 0 insts, 1 shapes (1 times)\n" "CHILD1[1] 0 insts, 4 shapes (2 times)\n" + "RING[1] 0 insts, 1 shapes (1 times)\n" ); } @@ -1054,8 +1054,8 @@ TEST(BasicSelfOverlapWithSize9) // from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than // 6. And the shapes from top inside the ring are not seen by the RING's subject shapes. "TOP[1] 0 insts, 0 shapes (1 times)\n" - "RING[1] 0 insts, 1 shapes (1 times)\n" "CHILD1[1] 0 insts, 6 shapes (2 times)\n" + "RING[1] 0 insts, 1 shapes (1 times)\n" ); } diff --git a/src/lay/lay/layGSIHelpProvider.cc b/src/lay/lay/layGSIHelpProvider.cc index 171b3fc6a..b44c91a4a 100644 --- a/src/lay/lay/layGSIHelpProvider.cc +++ b/src/lay/lay/layGSIHelpProvider.cc @@ -318,6 +318,55 @@ real_class (const gsi::ClassBase *cls) return cls->declaration () ? cls->declaration () : cls; } +namespace { + +class RecursiveClassIterator +{ +public: + typedef const gsi::ClassBase &reference; + typedef const gsi::ClassBase *pointer; + + RecursiveClassIterator () + { + if (gsi::ClassBase::begin_classes () != gsi::ClassBase::end_classes ()) { + m_cls_iter_stack.push_back (std::make_pair (gsi::ClassBase::begin_classes (), gsi::ClassBase::end_classes ())); + } + } + + bool at_end () const + { + return m_cls_iter_stack.empty (); + } + + RecursiveClassIterator &operator++ () + { + if (operator* ().begin_child_classes () != operator* ().end_child_classes ()) { + m_cls_iter_stack.push_back (std::make_pair (operator* ().begin_child_classes (), operator* ().end_child_classes ())); + } else { + while (! m_cls_iter_stack.empty () && ++m_cls_iter_stack.back ().first == m_cls_iter_stack.back ().second) { + m_cls_iter_stack.pop_back (); + } + } + + return *this; + } + + const gsi::ClassBase &operator* () const + { + return *m_cls_iter_stack.back ().first; + } + + const gsi::ClassBase *operator-> () const + { + return m_cls_iter_stack.back ().first.operator-> (); + } + +private: + std::list > m_cls_iter_stack; +}; + +} + static std::string replace_references (const std::string &t, const gsi::ClassBase *cls_base) { @@ -341,7 +390,7 @@ replace_references (const std::string &t, const gsi::ClassBase *cls_base) r += std::string (t, q, p - q); size_t pp = ++p; - while (p < t.size () && (t[p] == '_' || isalnum (t [p]))) { + while (p < t.size () && (t[p] == '_' || t[p] == ':' || isalnum (t [p]))) { ++p; } if (p < t.size () && (t[p] == '?' || t [p] == '=')) { @@ -369,8 +418,8 @@ replace_references (const std::string &t, const gsi::ClassBase *cls_base) found = true; } - for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes () && !found; ++c) { - if (c->name () == id) { + for (RecursiveClassIterator c; ! c.at_end (); ++c) { + if (c->qname () == id) { r += " (); 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); + 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 & @@ -259,7 +259,7 @@ GDS2ReaderText::get_double () } void -GDS2ReaderText::get_string (tl::string &s) const +GDS2ReaderText::get_string (std::string &s) const { // TODO: get rid of this const_cast hack s = (const_cast (this))->reader.skip (); diff --git a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h index 4b394d13b..56de72863 100644 --- a/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h +++ b/src/plugins/streamers/gds2/db_plugin/contrib/dbGDS2TextReader.h @@ -112,7 +112,7 @@ private: virtual std::string path () const; const char *get_string (); - void get_string (tl::string &s) const; + void get_string (std::string &s) const; int get_int (); short get_short (); unsigned short get_ushort (); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc index f0fc9ac22..772da9429 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.cc @@ -64,7 +64,7 @@ GDS2Reader::read (db::Layout &layout, const db::LoadLayoutOptions &options) --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); + 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 & @@ -210,7 +210,7 @@ GDS2Reader::get_string () } void -GDS2Reader::get_string (tl::string &s) const +GDS2Reader::get_string (std::string &s) const { s.assign ((const char *) mp_rec_buf, 0, m_reclen); } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h index e5bbb4aa3..2b51144dd 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Reader.h @@ -126,7 +126,7 @@ private: virtual std::string path () const; virtual const char *get_string (); - virtual void get_string (tl::string &s) const; + virtual void get_string (std::string &s) const; virtual int get_int (); virtual short get_short (); virtual unsigned short get_ushort (); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index 1541960e0..d79793638 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -84,7 +84,7 @@ GDS2ReaderBase::~GDS2ReaderBase () } 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) +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) { m_layer_map = layer_map; m_layer_map.prepare (layout); @@ -95,9 +95,17 @@ GDS2ReaderBase::basic_read (db::Layout &layout, const LayerMap &layer_map, bool m_box_mode = box_mode; m_create_layers = create_other_layers; + set_cell_conflict_resolution (cc_resolution); + layout.start_changes (); - do_read (layout); - layout.end_changes (); + try { + do_read (layout); + finish (layout); + layout.end_changes (); + } catch (...) { + layout.end_changes (); + throw; + } return m_layer_map; } @@ -235,7 +243,6 @@ GDS2ReaderBase::do_read (db::Layout &layout) { m_cellname = ""; m_libname = ""; - m_mapped_cellnames.clear (); // read header if (get_record () != sHEADER) { @@ -349,7 +356,7 @@ GDS2ReaderBase::do_read (db::Layout &layout) } else { - db::cell_index_type cell_index = make_cell (layout, m_cellname.c_str (), false); + db::cell_index_type cell_index = make_cell (layout, m_cellname); db::Cell *cell = &layout.cell (cell_index); @@ -359,8 +366,6 @@ GDS2ReaderBase::do_read (db::Layout &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; - // marks the cell for begin addressed by REF's despite being a proxy: - m_mapped_cellnames.insert (std::make_pair (m_cellname, m_cellname)); } } @@ -973,54 +978,6 @@ GDS2ReaderBase::read_box (db::Layout &layout, db::Cell &cell) } } -db::cell_index_type -GDS2ReaderBase::make_cell (db::Layout &layout, const char *cn, bool for_instance) -{ - db::cell_index_type ci = 0; - - // map to the real name which maybe a different one due to localization - // of proxy cells (they are not to be reopened) - bool is_mapped = false; - if (! m_mapped_cellnames.empty ()) { - std::map::const_iterator n = m_mapped_cellnames.find (cn); - if (n != m_mapped_cellnames.end ()) { - cn = n->second.c_str (); - is_mapped = true; - } - } - - std::pair c = layout.cell_by_name (cn); - if (c.first && (is_mapped || ! layout.cell (c.second).is_proxy ())) { - - // cell already there: just add instance (cell might have been created through forward reference) - // NOTE: we don't address "reopened" proxies as proxies are always local to a layout - - ci = c.second; - - // mark the cell as read - if (! for_instance) { - layout.cell (ci).set_ghost_cell (false); - } - - } else { - - ci = layout.add_cell (cn); - - if (for_instance) { - // mark this cell a "ghost cell" until it's actually read - layout.cell (ci).set_ghost_cell (true); - } - - if (c.first) { - // this cell has been given a new name: remember this name for localization - m_mapped_cellnames.insert (std::make_pair (cn, layout.cell_name (ci))); - } - - } - - return ci; -} - void GDS2ReaderBase::read_ref (db::Layout &layout, db::Cell & /*cell*/, bool array, tl::vector &instances, tl::vector &instances_with_props) { @@ -1033,7 +990,7 @@ GDS2ReaderBase::read_ref (db::Layout &layout, db::Cell & /*cell*/, bool array, t error (tl::to_string (tr ("SNAME record expected"))); } - db::cell_index_type ci = make_cell (layout, get_string (), true); + db::cell_index_type ci = cell_for_instance (layout, get_string ()); bool mirror = false; int angle = 0; diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h index 829ccea71..a094da9fb 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h @@ -29,6 +29,7 @@ #include "dbLayout.h" #include "dbReader.h" #include "dbStreamLayers.h" +#include "dbCommonReader.h" #include "tlException.h" #include "tlInternational.h" @@ -49,7 +50,7 @@ struct GDS2XY * @brief The GDS2 format basic stream reader */ class DB_PLUGIN_PUBLIC GDS2ReaderBase - : public ReaderBase + : public CommonReader { public: /** @@ -86,20 +87,21 @@ protected: * @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); + 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 tl::string &cellname () const { return m_cellname; } + const std::string &cellname () const { return m_cellname; } private: friend class GDS2ReaderLayerMapping; LayerMap m_layer_map; - tl::string m_cellname; + std::string m_cellname; std::string m_libname; double m_dbu, m_dbuu; bool m_create_layers; @@ -109,7 +111,6 @@ private: unsigned int m_box_mode; std::map > m_context_info; std::vector m_all_points; - std::map m_mapped_cellnames; void read_context_info_cell (); void read_boundary (db::Layout &layout, db::Cell &cell, bool from_box_record); @@ -117,7 +118,6 @@ private: void read_text (db::Layout &layout, db::Cell &cell); 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); - db::cell_index_type make_cell (db::Layout &layout, const char *cn, bool for_instance); void do_read (db::Layout &layout); @@ -125,12 +125,15 @@ private: std::pair finish_element (db::PropertiesRepository &rep); void finish_element (); + virtual void common_reader_error (const std::string &msg) { error (msg); } + virtual void common_reader_warn (const std::string &msg) { warn (msg); } + virtual void error (const std::string &txt) = 0; virtual void warn (const std::string &txt) = 0; virtual std::string path () const = 0; virtual const char *get_string () = 0; - virtual void get_string (tl::string &s) const = 0; + virtual void get_string (std::string &s) const = 0; virtual int get_int () = 0; virtual short get_short () = 0; virtual unsigned short get_ushort () = 0; diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc index b1971b0e4..838260bf3 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc @@ -366,6 +366,8 @@ TEST(Bug_121_1) reader.read (layout); } + fflush(stdout); + std::string fn_au (tl::testsrc () + "/testdata/gds/bug_121_au1.gds"); db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); } @@ -434,3 +436,100 @@ TEST(3_AdvancedMapping) std::string fn_au (tl::testsrc () + "/testdata/gds/alm_au.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; + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + std::string fn_au (tl::testsrc () + "/testdata/gds/collect_rename_au.gds"); + db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); +} + +TEST(4_CollectModeOverwrite) +{ + db::Manager m (false); + db::Layout layout (&m); + + db::LoadLayoutOptions options; + options.get_options ().cell_conflict_resolution = db::CommonReader::OverwriteCell; + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + std::string fn_au (tl::testsrc () + "/testdata/gds/collect_overwrite_au.gds"); + db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); +} + +TEST(4_CollectModeSkip) +{ + db::Manager m (false); + db::Layout layout (&m); + + db::LoadLayoutOptions options; + options.get_options ().cell_conflict_resolution = db::CommonReader::SkipNewCell; + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + std::string fn_au (tl::testsrc () + "/testdata/gds/collect_skip_au.gds"); + db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); +} + +TEST(4_CollectModeAdd) +{ + db::Manager m (false); + db::Layout layout (&m); + + db::LoadLayoutOptions options; + options.get_options ().cell_conflict_resolution = db::CommonReader::AddToCell; + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + std::string fn_au (tl::testsrc () + "/testdata/gds/collect_add_au.gds"); + db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); +} + diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index d7b4fd67d..3bf5485a5 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -144,9 +144,12 @@ OASISReader::read (db::Layout &layout, const db::LoadLayoutOptions &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 (); @@ -758,17 +761,11 @@ OASISReader::do_read (db::Layout &layout) id_mode propstring_id_mode = any; id_mode propname_id_mode = any; - m_cellnames.clear (); m_cellname_properties.clear (); m_textstrings.clear (); m_propstrings.clear (); m_propnames.clear (); m_layernames.clear (); - m_cells_by_id.clear (); - m_cells_by_name.clear (); - m_defined_cells_by_id.clear (); - m_defined_cells_by_name.clear (); - m_mapped_cellnames.clear (); m_instances.clear (); m_instances_with_props.clear (); @@ -826,9 +823,7 @@ OASISReader::do_read (db::Layout &layout) get (id); } - if (! m_cellnames.insert (std::make_pair (id, name)).second) { - error (tl::sprintf (tl::to_string (tr ("A CELLNAME with id %ld is present already")), id)); - } + rename_cell (layout, id, name); reset_modal_variables (); @@ -1150,36 +1145,17 @@ OASISReader::do_read (db::Layout &layout) unsigned long id = 0; get (id); - if (! m_defined_cells_by_id.insert (id).second) { + + std::pair cc = cell_by_id (id); + if (cc.first && ! layout.cell (cc.second).is_ghost_cell ()) { error (tl::sprintf (tl::to_string (tr ("A cell with id %ld is defined already")), id)); } - std::map ::const_iterator c = m_cells_by_id.find (id); - if (c != m_cells_by_id.end ()) { - - cell_index = c->second; - layout.cell (cell_index).set_ghost_cell (false); - - } else { - - std::map ::const_iterator name = m_cellnames.find (id); - if (name == m_cellnames.end ()) { - - cell_index = layout.add_cell (); - // force a cell rename to empty to avoid name clashes of the generated - // $x names with the same inside the OASIS file. - layout.rename_cell (cell_index, ""); - m_forward_references.insert (std::make_pair (id, cell_index)); - - } else { - - cell_index = make_cell (layout, name->second.c_str (), false); - m_cells_by_name.insert (std::make_pair (name->second, cell_index)); - - } - - m_cells_by_id.insert (std::make_pair (id, cell_index)); + cell_index = make_cell (layout, id); + m_cellname = name_for_id (id); + if (m_cellname.empty ()) { + m_cellname = std::string ("#") + tl::to_string (id); } } else { @@ -1189,22 +1165,15 @@ OASISReader::do_read (db::Layout &layout) } std::string name = get_str (); - if (! m_defined_cells_by_name.insert (name).second) { + + std::pair cc = cell_by_name (name); + if (cc.first && ! layout.cell (cc.second).is_ghost_cell ()) { error (tl::sprintf (tl::to_string (tr ("A cell with name %s is defined already")), name.c_str ())); } - std::map ::const_iterator c = m_cells_by_name.find (name); - if (c != m_cells_by_name.end ()) { + cell_index = make_cell (layout, name); - cell_index = c->second; - layout.cell (cell_index).set_ghost_cell (false); - - } else { - - cell_index = make_cell (layout, name.c_str (), false); - m_cells_by_name.insert (std::make_pair (name, cell_index)); - - } + m_cellname = name; } @@ -1326,72 +1295,16 @@ OASISReader::do_read (db::Layout &layout) } - for (std::map ::const_iterator fw = m_forward_references.begin (); fw != m_forward_references.end (); ++fw) { - - std::map ::const_iterator cn = m_cellnames.find (fw->first); - if (cn == m_cellnames.end ()) { - - error (tl::sprintf (tl::to_string (tr ("No cellname defined for cell name id %ld")), fw->first)); - - } else { - - std::pair c = layout.cell_by_name (cn->second.c_str ()); - if (c.first) { - - // needed, since we have disabled updates - layout.force_update (); - - // add-on reading of forward-referenced cell: need to copy the new cell to the original one plus - // change instances of the new cell and delete the new cell then. - - const db::Cell &new_cell = layout.cell (fw->second); - db::Cell &org_cell = layout.cell (c.second); - - // copy over the instances - for (db::Cell::const_iterator i = new_cell.begin (); ! i.at_end (); ++i) { - org_cell.insert (*i); - } - - // copy over the shapes - for (unsigned int l = 0; l < layout.layers (); ++l) { - if (layout.is_valid_layer (l) && ! new_cell.shapes (l).empty ()) { - org_cell.shapes (l).insert (new_cell.shapes (l)); - } - } - - // replace all instances of the new cell with the original one - std::vector > parents; - for (db::Cell::parent_inst_iterator pi = new_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { - parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ())); - } - - for (std::vector >::const_iterator p = parents.begin (); p != parents.end (); ++p) { - db::CellInstArray ia = p->second.cell_inst (); - ia.object ().cell_index (org_cell.cell_index ()); - layout.cell (p->first).replace (p->second, ia); - } - - // finally delete the new cell - layout.delete_cell (new_cell.cell_index ()); - - } else { - layout.rename_cell (fw->second, cn->second.c_str ()); - } - - } - - } - // attach the properties found in CELLNAME to the cells (which may have other properties) for (std::map::const_iterator p = m_cellname_properties.begin (); p != m_cellname_properties.end (); ++p) { - std::map ::const_iterator c = m_cells_by_id.find (p->first); - if (c != m_cells_by_id.end ()) { + std::pair c = cell_by_id (p->first); + if (c.first) { db::PropertiesRepository::properties_set cnp = layout.properties_repository ().properties (p->second); // Merge existing properties with the ones from CELLNAME - db::Cell &cell = layout.cell (c->second); + db::Cell &cell = layout.cell (c.second); if (cell.prop_id () != 0) { db::PropertiesRepository::properties_set cp = layout.properties_repository ().properties (cell.prop_id ()); cnp.insert (cp.begin (), cp.end ()); @@ -1844,54 +1757,6 @@ OASISReader::read_repetition () return mm_repetition.get ().size () > 1; } -db::cell_index_type -OASISReader::make_cell (db::Layout &layout, const char *cn, bool for_instance) -{ - db::cell_index_type ci = 0; - - // map to the real name which maybe a different one due to localization - // of proxy cells (they are not to be reopened) - bool is_mapped = false; - if (! m_mapped_cellnames.empty ()) { - std::map::const_iterator n = m_mapped_cellnames.find (cn); - if (n != m_mapped_cellnames.end ()) { - cn = n->second.c_str (); - is_mapped = true; - } - } - - std::pair c = layout.cell_by_name (cn); - if (c.first && (is_mapped || ! layout.cell (c.second).is_proxy ())) { - - // cell already there: just add instance (cell might have been created through forward reference) - // NOTE: we don't address "reopened" proxies as proxies are always local to a layout - - ci = c.second; - - // mark the cell as read - if (! for_instance) { - layout.cell (ci).set_ghost_cell (false); - } - - } else { - - ci = layout.add_cell (cn); - - if (for_instance) { - // mark this cell a "ghost cell" until it's actually read - layout.cell (ci).set_ghost_cell (true); - } - - if (c.first) { - // this cell has been given a new name: remember this name for localization - m_mapped_cellnames.insert (std::make_pair (cn, layout.cell_name (ci))); - } - - } - - return ci; -} - void OASISReader::do_read_placement (unsigned char r, bool xy_absolute, @@ -1909,31 +1774,8 @@ OASISReader::do_read_placement (unsigned char r, // cell by id unsigned long id; get (id); - std::map ::const_iterator cid = m_cells_by_id.find (id); - if (cid == m_cells_by_id.end ()) { - // create the cell - std::map ::const_iterator name = m_cellnames.find (id); - if (name == m_cellnames.end ()) { - - mm_placement_cell = layout.add_cell (); - m_forward_references.insert (std::make_pair (id, mm_placement_cell.get ())); - - // temporarily mark as "ghost cell" - layout.cell (mm_placement_cell.get ()).set_ghost_cell (true); - - } else { - - mm_placement_cell = make_cell (layout, name->second.c_str (), true); - m_cells_by_name.insert (std::make_pair (name->second, mm_placement_cell.get ())); - - } - - m_cells_by_id.insert (std::make_pair (id, mm_placement_cell.get ())); - - } else { - mm_placement_cell = cid->second; - } + mm_placement_cell = cell_for_instance (layout, id); } else { @@ -1941,15 +1783,7 @@ OASISReader::do_read_placement (unsigned char r, std::string name; get_str (name); - std::map ::const_iterator cid = m_cells_by_name.find (name); - if (cid == m_cells_by_name.end ()) { - - mm_placement_cell = make_cell (layout, name.c_str (), true); - m_cells_by_name.insert (std::make_pair (name, mm_placement_cell.get ())); - - } else { - mm_placement_cell = cid->second; - } + mm_placement_cell = cell_for_instance (layout, name); } @@ -3433,7 +3267,6 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) m_instances_with_props.clear (); m_progress.set (m_stream.pos ()); - m_cellname = layout.cell_name (cell_index); bool xy_absolute = true; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h index 63315e78a..a8a56e173 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h @@ -33,6 +33,7 @@ #include "dbOASISFormat.h" #include "dbStreamLayers.h" #include "dbPropertiesRepository.h" +#include "dbCommonReader.h" #include "tlException.h" #include "tlInternational.h" @@ -62,7 +63,7 @@ public: * @brief The OASIS format stream reader */ class DB_PLUGIN_PUBLIC OASISReader - : public ReaderBase, + : public CommonReader, public OASISDiagnostics { public: @@ -118,6 +119,7 @@ public: */ virtual const char *format () const { return "OASIS"; } +protected: /** * @brief Issue an error with positional information * @@ -132,6 +134,9 @@ public: */ virtual void warn (const std::string &txt); + virtual void common_reader_error (const std::string &msg) { error (msg); } + virtual void common_reader_warn (const std::string &msg) { warn (msg); } + private: friend class OASISReaderLayerMapping; @@ -194,7 +199,6 @@ private: modal_variable mm_last_property_is_sprop; modal_variable mm_last_value_list; - std::map m_cellnames; std::map m_cellname_properties; std::map m_textstrings; std::map m_text_forward_references; @@ -205,18 +209,11 @@ private: tl::vector m_instances; tl::vector m_instances_with_props; - std::map m_cells_by_id; - std::map m_forward_references; - std::map m_cells_by_name; bool m_create_layers; bool m_read_texts; bool m_read_properties; bool m_read_all_properties; - std::set m_defined_cells_by_id; - std::set m_defined_cells_by_name; - std::map m_mapped_cellnames; - std::map m_propname_forward_references; std::map m_propvalue_forward_references; db::property_names_id_type m_s_gds_property_name_id; @@ -238,7 +235,6 @@ private: void do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout); void do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout); void do_read_circle (bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout); - db::cell_index_type make_cell (db::Layout &layout, const char *cn, bool for_instance); void reset_modal_variables (); diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index a3e5b8c21..667c80b80 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1150,6 +1150,18 @@ OASISWriter::reset_modal_variables () mm_last_value_list.reset (); } +static bool must_write_cell (const db::Cell &cref) +{ + // Don't write proxy cells which are not employed + return ! cref.is_proxy () || ! cref.is_top (); +} + +static bool skip_cell_body (const db::Cell &cref) +{ + // Skip cell bodies for ghost cells unless empty (they are not longer ghost cells in this case) + return cref.is_ghost_cell () && cref.empty (); +} + void OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) { @@ -1184,13 +1196,13 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save cells_by_index.reserve (cell_set.size ()); for (db::Layout::bottom_up_const_iterator cell = layout.begin_bottom_up (); cell != layout.end_bottom_up (); ++cell) { - if (cell_set.find (*cell) != cell_set.end ()) { + if (cell_set.find (*cell) != cell_set.end () && must_write_cell (layout.cell (*cell))) { cells.push_back (*cell); } } for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) { - if (cell_set.find (cell->cell_index ()) != cell_set.end ()) { + if (cell_set.find (cell->cell_index ()) != cell_set.end () && must_write_cell (layout.cell (cell->cell_index ()))) { cells_by_index.push_back (cell->cell_index ()); } } @@ -1594,80 +1606,79 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save const db::Cell &cref (layout.cell (*cell)); mp_cell = &cref; - // don't write ghost cells unless they are not empty (any more) - // also don't write proxy cells which are not employed - if ((! cref.is_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) { + // skip cell body if the cell is not to be written + if (skip_cell_body (cref)) { + continue; + } - // cell header + // cell header - cell_positions.insert (std::make_pair (*cell, mp_stream->pos ())); + cell_positions.insert (std::make_pair (*cell, mp_stream->pos ())); - write_record_id (13); // CELL - write ((unsigned long) *cell); + write_record_id (13); // CELL + write ((unsigned long) *cell); - reset_modal_variables (); + reset_modal_variables (); - if (m_options.write_cblocks) { - begin_cblock (); - } + if (m_options.write_cblocks) { + begin_cblock (); + } - // context information as property named KLAYOUT_CONTEXT - if (cref.is_proxy () && options.write_context_info ()) { + // context information as property named KLAYOUT_CONTEXT + if (cref.is_proxy () && options.write_context_info ()) { - context_prop_strings.clear (); + context_prop_strings.clear (); - if (layout.get_context_info (*cell, context_prop_strings)) { + if (layout.get_context_info (*cell, context_prop_strings)) { - write_record_id (28); - write_byte (char (0xf6)); - std::map ::const_iterator pni = m_propnames.find (klayout_context_name); - tl_assert (pni != m_propnames.end ()); - write (pni->second); + write_record_id (28); + write_byte (char (0xf6)); + std::map ::const_iterator pni = m_propnames.find (klayout_context_name); + tl_assert (pni != m_propnames.end ()); + write (pni->second); - write ((unsigned long) context_prop_strings.size ()); - - for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { - write_byte (14); // b-string by reference number - std::map ::const_iterator psi = m_propstrings.find (*c); - tl_assert (psi != m_propstrings.end ()); - write (psi->second); - } - - mm_last_property_name = klayout_context_name; - mm_last_property_is_sprop = false; - mm_last_value_list.reset (); + write ((unsigned long) context_prop_strings.size ()); + for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { + write_byte (14); // b-string by reference number + std::map ::const_iterator psi = m_propstrings.find (*c); + tl_assert (psi != m_propstrings.end ()); + write (psi->second); } + mm_last_property_name = klayout_context_name; + mm_last_property_is_sprop = false; + mm_last_value_list.reset (); + } - if (cref.prop_id () != 0) { - write_props (cref.prop_id ()); - } - - // instances - if (cref.cell_instances () > 0) { - write_insts (cell_set); - } - - // shapes - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - const db::Shapes &shapes = cref.shapes (l->first); - if (! shapes.empty ()) { - write_shapes (l->second, shapes); - m_progress.set (mp_stream->pos ()); - } - } - - // end CBLOCK if required - if (m_options.write_cblocks) { - end_cblock (); - } - - // end of cell - } + if (cref.prop_id () != 0) { + write_props (cref.prop_id ()); + } + + // instances + if (cref.cell_instances () > 0) { + write_insts (cell_set); + } + + // shapes + for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { + const db::Shapes &shapes = cref.shapes (l->first); + if (! shapes.empty ()) { + write_shapes (l->second, shapes); + m_progress.set (mp_stream->pos ()); + } + } + + // end CBLOCK if required + if (m_options.write_cblocks) { + end_cblock (); + } + + // end of cell + } // write cell table at the end in strict mode (in that mode we need the cell positions diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc index ce4f6c479..9e7e48aef 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriter.cc @@ -570,11 +570,11 @@ TEST(100) const char *expected = "begin_lib 0.001\n" + "begin_cell {$4}\n" + "end_cell\n" "begin_cell {$1}\n" "box 1 0 {0 100} {1000 1200}\n" "end_cell\n" - "begin_cell {$4}\n" - "end_cell\n" "begin_cell {$3}\n" "sref {$1} 90 1 1 {-10 20}\n" "sref {$4} 90 1 1 {-10 20}\n" @@ -1327,14 +1327,14 @@ TEST(116) " {{name} {117}}\n" "}\n" "begin_libp $props 0.001\n" + "begin_cell {$2}\n" + "end_cell\n" "set props {\n" " {42 {42}}\n" "}\n" "begin_cellp $props {$1}\n" "path 1 0 0 0 0 {0 100} {1000 1200}\n" "end_cell\n" - "begin_cell {$2}\n" - "end_cell\n" "end_lib\n" ; @@ -1376,14 +1376,14 @@ TEST(116) " {{name} {117}}\n" "}\n" "begin_libp $props 0.001\n" + "begin_cell {$2}\n" + "end_cell\n" "set props {\n" " {42 {42}}\n" "}\n" "begin_cellp $props {$1}\n" "path 1 0 0 0 0 {0 100} {1000 1200}\n" "end_cell\n" - "begin_cell {$2}\n" - "end_cell\n" "end_lib\n" ; @@ -1431,17 +1431,17 @@ TEST(116) "}\n" "begin_libp $props 0.001\n" "set props {\n" + " {{S_BOUNDING_BOX} {2,0,0,0,0}}\n" + "}\n" + "begin_cellp $props {$2}\n" + "end_cell\n" + "set props {\n" " {{S_BOUNDING_BOX} {0,0,100,1000,1100}}\n" " {42 {42}}\n" "}\n" "begin_cellp $props {$1}\n" "path 1 0 0 0 0 {0 100} {1000 1200}\n" "end_cell\n" - "set props {\n" - " {{S_BOUNDING_BOX} {2,0,0,0,0}}\n" - "}\n" - "begin_cellp $props {$2}\n" - "end_cell\n" "end_lib\n" ; diff --git a/testdata/gds/collect_add_au.gds b/testdata/gds/collect_add_au.gds new file mode 100644 index 000000000..ca0206610 Binary files /dev/null and b/testdata/gds/collect_add_au.gds differ diff --git a/testdata/gds/collect_added.gds b/testdata/gds/collect_added.gds new file mode 100644 index 000000000..e4882b6d8 Binary files /dev/null and b/testdata/gds/collect_added.gds differ diff --git a/testdata/gds/collect_basic.gds b/testdata/gds/collect_basic.gds new file mode 100644 index 000000000..9fad7517e Binary files /dev/null and b/testdata/gds/collect_basic.gds differ diff --git a/testdata/gds/collect_overwrite_au.gds b/testdata/gds/collect_overwrite_au.gds new file mode 100644 index 000000000..e4790a23d Binary files /dev/null and b/testdata/gds/collect_overwrite_au.gds differ diff --git a/testdata/gds/collect_rename_au.gds b/testdata/gds/collect_rename_au.gds new file mode 100644 index 000000000..af98c817f Binary files /dev/null and b/testdata/gds/collect_rename_au.gds differ diff --git a/testdata/gds/collect_skip_au.gds b/testdata/gds/collect_skip_au.gds new file mode 100644 index 000000000..5a0b96fa9 Binary files /dev/null and b/testdata/gds/collect_skip_au.gds differ diff --git a/testdata/oasis/t2.4.ot b/testdata/oasis/t2.4.ot index 8a625c7cf..d1def8758 100644 --- a/testdata/oasis/t2.4.ot +++ b/testdata/oasis/t2.4.ot @@ -7,10 +7,10 @@ # Explicit assignment of ID's # # begin_lib 0.001 -# begin_cell {ABC} -# end_cell # begin_cell {XYZ} # end_cell +# begin_cell {ABC} +# end_cell # end_lib # # diff --git a/testdata/oasis/t2.4_au.txt b/testdata/oasis/t2.4_au.txt index ede7f7760..aee39fbb5 100644 --- a/testdata/oasis/t2.4_au.txt +++ b/testdata/oasis/t2.4_au.txt @@ -1,6 +1,6 @@ begin_lib 0.001 -begin_cell {ABC} -end_cell begin_cell {XYZ} end_cell +begin_cell {ABC} +end_cell end_lib diff --git a/testdata/oasis/t2.6_au.txt b/testdata/oasis/t2.6_au.txt index 3c0ebf770..ec8221a98 100644 --- a/testdata/oasis/t2.6_au.txt +++ b/testdata/oasis/t2.6_au.txt @@ -1,6 +1,6 @@ begin_lib 0.001 -begin_cell {ABC} -end_cell begin_cell { XYZ} end_cell +begin_cell {ABC} +end_cell end_lib diff --git a/testdata/ruby/dbReaders.rb b/testdata/ruby/dbReaders.rb index f96956f99..0591ade21 100644 --- a/testdata/ruby/dbReaders.rb +++ b/testdata/ruby/dbReaders.rb @@ -53,6 +53,14 @@ class DBReaders_TestClass < TestBase opt.properties_enabled = false assert_equal(opt.properties_enabled?, false) + assert_equal(opt.cell_conflict_resolution, RBA::LoadLayoutOptions::AddToCell) + opt.cell_conflict_resolution = RBA::LoadLayoutOptions::OverwriteCell + assert_equal(opt.cell_conflict_resolution, RBA::LoadLayoutOptions::OverwriteCell) + opt.cell_conflict_resolution = RBA::LoadLayoutOptions::SkipNewCell + assert_equal(opt.cell_conflict_resolution, RBA::LoadLayoutOptions::SkipNewCell) + opt.cell_conflict_resolution = RBA::LoadLayoutOptions::RenameCell + assert_equal(opt.cell_conflict_resolution, RBA::LoadLayoutOptions::RenameCell) + end # GDS2 Options