diff --git a/src/db/db/dbCellMapping.h b/src/db/db/dbCellMapping.h index a6b1f24a4..77b134d6e 100644 --- a/src/db/db/dbCellMapping.h +++ b/src/db/db/dbCellMapping.h @@ -144,7 +144,7 @@ public: */ void map (db::cell_index_type cell_index_b, db::cell_index_type cell_index_a) { - m_b2a_mapping.insert (std::make_pair (cell_index_b, cell_index_a)); + m_b2a_mapping.insert (std::make_pair (cell_index_b, 0)).first->second = cell_index_a; } /** diff --git a/src/db/db/dbLayerMapping.h b/src/db/db/dbLayerMapping.h index 8e0942386..7729d0739 100644 --- a/src/db/db/dbLayerMapping.h +++ b/src/db/db/dbLayerMapping.h @@ -102,7 +102,7 @@ public: */ void map (unsigned int layer_b, unsigned int layer_a) { - m_b2a_mapping.insert (std::make_pair (layer_b, layer_a)); + m_b2a_mapping.insert (std::make_pair (layer_b, 0)).first->second = layer_a; } /** diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index a41cef07e..1e37a2f59 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -126,6 +126,40 @@ PropertyMapper::operator() (db::Layout::properties_id_type source_id) // ------------------------------------------------------------------------------------ // merge_layouts implementation +static void +collect_cells_to_copy (const db::Layout &source, + const std::vector &source_cells, + const std::map &cell_mapping, + std::set &all_top_level_cells, + std::set &all_cells_to_copy) +{ + std::vector dropped_cells; + + for (std::map::const_iterator m = cell_mapping.begin (); m != cell_mapping.end (); ++m) { + if (m->second == DropCell) { + dropped_cells.push_back (m->first); + } + } + + for (std::vector::const_iterator src = source_cells.begin (); src != source_cells.end (); ++src) { + + all_cells_to_copy.insert (*src); + all_top_level_cells.insert (*src); + + // feed the excluded cells into the "all_cells_to_copy" cache. This will make "collect_called_cells" not + // dive into their hierarchy. We will later delete them there. + all_cells_to_copy.insert (dropped_cells.begin (), dropped_cells.end ()); + + source.cell (*src).collect_called_cells (all_cells_to_copy); + + for (std::vector::const_iterator i = dropped_cells.begin (); i != dropped_cells.end (); ++i) { + all_cells_to_copy.erase (*i); + all_top_level_cells.erase (*i); + } + + } +} + void merge_layouts (db::Layout &target, const db::Layout &source, @@ -135,19 +169,11 @@ merge_layouts (db::Layout &target, const std::map &layer_mapping, std::map *final_cell_mapping) { - if (final_cell_mapping) { - final_cell_mapping->insert (cell_mapping.begin (), cell_mapping.end ()); - } - // collect all called cells and all top level cells std::set all_top_level_cells; std::set all_cells_to_copy; - for (std::vector::const_iterator src = source_cells.begin (); src != source_cells.end (); ++src) { - all_cells_to_copy.insert (*src); - all_top_level_cells.insert (*src); - source.cell (*src).collect_called_cells (all_cells_to_copy); - } + collect_cells_to_copy (source, source_cells, cell_mapping, all_top_level_cells, all_cells_to_copy); // identify all new cells and create new ones std::map new_cell_mapping; @@ -158,6 +184,11 @@ merge_layouts (db::Layout &target, } if (final_cell_mapping) { + for (std::map::const_iterator m = cell_mapping.begin (); m != cell_mapping.end (); ++m) { + if (m->second != DropCell) { + final_cell_mapping->insert (*m); + } + } final_cell_mapping->insert (new_cell_mapping.begin (), new_cell_mapping.end ()); } @@ -248,7 +279,7 @@ copy_or_propagate_shapes (db::Layout &target, } - } else { + } else if (cm->second != DropCell) { db::Cell &target_cell = target.cell (cm->second); target_cell.shapes (target_layer).insert_transformed (source_cell.shapes (source_layer), trans * propagate_trans, pm); @@ -256,23 +287,20 @@ copy_or_propagate_shapes (db::Layout &target, } } -void -copy_shapes (db::Layout &target, - const db::Layout &source, - const db::ICplxTrans &trans, - const std::vector &source_cells, - const std::map &cell_mapping, - const std::map &layer_mapping) +static void +copy_or_move_shapes (db::Layout &target, + db::Layout &source, + const db::ICplxTrans &trans, + const std::vector &source_cells, + const std::map &cell_mapping, + const std::map &layer_mapping, + bool move) { // collect all called cells and all top level cells std::set all_top_level_cells; std::set all_cells_to_copy; - for (std::vector::const_iterator src = source_cells.begin (); src != source_cells.end (); ++src) { - all_cells_to_copy.insert (*src); - all_top_level_cells.insert (*src); - source.cell (*src).collect_called_cells (all_cells_to_copy); - } + collect_cells_to_copy (source, source_cells, cell_mapping, all_top_level_cells, all_cells_to_copy); // provide the property mapper db::PropertyMapper pm (target, source); @@ -284,10 +312,24 @@ copy_shapes (db::Layout &target, for (std::map::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) { ++progress; copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping); + if (move) { + source.cell (*c).shapes (lm->first).clear (); + } } } } +void +copy_shapes (db::Layout &target, + const db::Layout &source, + const db::ICplxTrans &trans, + const std::vector &source_cells, + const std::map &cell_mapping, + const std::map &layer_mapping) +{ + copy_or_move_shapes (target, const_cast (source), trans, source_cells, cell_mapping, layer_mapping, false); +} + void move_shapes (db::Layout &target, db::Layout &source, @@ -296,29 +338,7 @@ move_shapes (db::Layout &target, const std::map &cell_mapping, const std::map &layer_mapping) { - // collect all called cells and all top level cells - std::set all_top_level_cells; - std::set all_cells_to_copy; - - for (std::vector::const_iterator src = source_cells.begin (); src != source_cells.end (); ++src) { - all_cells_to_copy.insert (*src); - all_top_level_cells.insert (*src); - source.cell (*src).collect_called_cells (all_cells_to_copy); - } - - // provide the property mapper - db::PropertyMapper pm (target, source); - - tl::RelativeProgress progress (tl::to_string (QObject::tr ("Merge cells")), all_cells_to_copy.size () * layer_mapping.size (), 1); - - // and copy - for (std::set::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) { - for (std::map::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) { - ++progress; - copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping); - source.cell (*c).shapes (lm->first).clear (); - } - } + copy_or_move_shapes (target, source, trans, source_cells, cell_mapping, layer_mapping, true); } // ------------------------------------------------------------ diff --git a/src/db/db/dbLayoutUtils.h b/src/db/db/dbLayoutUtils.h index 65e59668a..b821e0008 100644 --- a/src/db/db/dbLayoutUtils.h +++ b/src/db/db/dbLayoutUtils.h @@ -32,6 +32,7 @@ #include #include +#include namespace db { @@ -110,6 +111,14 @@ private: std::map m_prop_id_map; }; +/** + * @brief A constant describing "drop cell" mapping + * + * If used as the target cell index, this constant means "drop the cell". + * This cell and it's children will be dropped unless the children are used by other cells. + */ +const db::cell_index_type DropCell = std::numeric_limits::max (); + /** * @brief Merge one layout into another * diff --git a/src/db/db/gsiDeclDbCellMapping.cc b/src/db/db/gsiDeclDbCellMapping.cc index 035bcd25c..935ef25cf 100644 --- a/src/db/db/gsiDeclDbCellMapping.cc +++ b/src/db/db/gsiDeclDbCellMapping.cc @@ -23,6 +23,7 @@ #include "gsiDecl.h" #include "dbCellMapping.h" +#include "dbLayoutUtils.h" #include "dbLayout.h" #include @@ -30,9 +31,25 @@ namespace gsi { +static db::cell_index_type drop_cell_const () +{ + return db::DropCell; +} + Class decl_CellMapping ("CellMapping", + gsi::method ("DropCell", &drop_cell_const, + "@brief A constant indicating the reques to drop a cell\n" + "\n" + "If used as a pseudo-target for the cell mapping, this index indicates " + "that the cell shall be dropped rather than created on the target side " + "or skipped by flattening. Instead, all shapes of this cell are discarded " + "and it's children are not translated unless explicitly requested or " + "if required are children for other cells.\n" + "\n" + "This constant has been introduced in version 0.25." + ) + gsi::method ("for_single_cell", &db::CellMapping::create_single_mapping, - "@brief Initialize the cell mapping for top-level identity\n" + "@brief Initializes the cell mapping for top-level identity\n" "\n" "@args layout_a, cell_index_a, layout_b, cell_index_b\n" "@param layout_a The target layout.\n" @@ -42,13 +59,15 @@ Class decl_CellMapping ("CellMapping", "\n" "The cell mapping is created for cell_b to cell_a in the respective layouts. " "This method clears the mapping and creates one for the single cell pair. " - "In addition, this method completes the mapping by adding all the child cells " - "of cell_b to layout_a and creating the proper instances. " + "If used for \\Cell#copy_tree or \\Cell#move_tree, this cell mapping will essentially " + "flatten the cell.\n" + "\n" + "This method is equivalent to \\clear, followed by \\map(cell_index_a, cell_index_b).\n" "\n" "This method has been introduced in version 0.23." ) + gsi::method ("for_single_cell_full", &db::CellMapping::create_single_mapping_full, - "@brief Initialize the cell mapping for top-level identity\n" + "@brief Initializes the cell mapping for top-level identity\n" "\n" "@args layout_a, cell_index_a, layout_b, cell_index_b\n" "@param layout_a The target layout.\n" @@ -58,11 +77,13 @@ Class decl_CellMapping ("CellMapping", "\n" "The cell mapping is created for cell_b to cell_a in the respective layouts. " "This method clears the mapping and creates one for the single cell pair. " + "In addition and in contrast to \\for_single_cell, this method completes the mapping by adding all the child cells " + "of cell_b to layout_a and creating the proper instances. " "\n" "This method has been introduced in version 0.23." ) + gsi::method ("from_geometry_full", &db::CellMapping::create_from_geometry_full, - "@brief Initialize the cell mapping using the geometrical identity in full mapping mode\n" + "@brief Initializes the cell mapping using the geometrical identity in full mapping mode\n" "\n" "@args layout_a, cell_index_a, layout_b, cell_index_b\n" "@param layout_a The target layout.\n" @@ -83,7 +104,7 @@ Class decl_CellMapping ("CellMapping", "This method has been introduced in version 0.23." ) + gsi::method ("from_geometry", &db::CellMapping::create_from_geometry, - "@brief Initialize the cell mapping using the geometrical identity\n" + "@brief Initializes the cell mapping using the geometrical identity\n" "\n" "@args layout_a, cell_index_a, layout_b, cell_index_b\n" "@param layout_a The target layout.\n" @@ -99,7 +120,7 @@ Class decl_CellMapping ("CellMapping", "This method has been introduced in version 0.23." ) + gsi::method ("from_names", &db::CellMapping::create_from_names, - "@brief Initialize the cell mapping using the name identity\n" + "@brief Initializes the cell mapping using the name identity\n" "\n" "@args layout_a, cell_index_a, layout_b, cell_index_b\n" "@param layout_a The target layout.\n" @@ -114,7 +135,7 @@ Class decl_CellMapping ("CellMapping", "This method has been introduced in version 0.23." ) + gsi::method ("from_names_full", &db::CellMapping::create_from_names_full, - "@brief Initialize the cell mapping using the name identity in full mapping mode\n" + "@brief Initializes the cell mapping using the name identity in full mapping mode\n" "\n" "@args layout_a, cell_index_a, layout_b, cell_index_b\n" "@param layout_a The target layout.\n" @@ -139,12 +160,12 @@ Class decl_CellMapping ("CellMapping", "This method has been introduced in version 0.23." ) + gsi::method ("map", &db::CellMapping::map, - "@brief Explicitly specify a mapping.\n" + "@brief Explicitly specifies a mapping.\n" "\n" "@args cell_index_b, cell_index_a\n" "\n" "@param cell_index_b The index of the cell in layout B (the \"source\")\n" - "@param cell_index_a The index of the cell in layout A (the \"target\")\n" + "@param cell_index_a The index of the cell in layout A (the \"target\") - this index can be \\DropCell\n" "\n" "Beside using the mapping generator algorithms provided through \\from_names and \\from_geometry, " "it is possible to explicitly specify cell mappings using this method.\n" @@ -152,20 +173,25 @@ Class decl_CellMapping ("CellMapping", "This method has been introduced in version 0.23." ) + gsi::method ("has_mapping?", &db::CellMapping::has_mapping, - "@brief Determine if a cell of layout_b has a mapping to a layout_a cell.\n" + "@brief Returns as value indicating whether a cell of layout_b has a mapping to a layout_a cell.\n" "\n" "@args cell_index_b\n" "\n" "@param cell_index_b The index of the cell in layout_b whose mapping is requested.\n" "@return true, if the cell has a mapping\n" + "\n" + "Note that if the cell is supposed to be dropped (see \\DropCell), the respective " + "source cell will also be regarded \"mapped\", so has_mapping? will return true in this case.\n" ) + gsi::method ("cell_mapping", &db::CellMapping::cell_mapping, - "@brief Determine cell mapping of a layout_b cell to the corresponding layout_a cell.\n" + "@brief Determines cell mapping of a layout_b cell to the corresponding layout_a cell.\n" "\n" "@args cell_index_b\n" "\n" "@param cell_index_b The index of the cell in layout_b whose mapping is requested.\n" "@return The cell index in layout_a.\n" + "\n" + "Note that the returned index can be \\DropCell to indicate the cell shall be dropped." ), "@brief A cell mapping (source to target layout)\n" "\n" diff --git a/src/db/unit_tests/dbLayoutUtils.cc b/src/db/unit_tests/dbLayoutUtils.cc new file mode 100644 index 000000000..b09633e8d --- /dev/null +++ b/src/db/unit_tests/dbLayoutUtils.cc @@ -0,0 +1,616 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2017 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 "dbLayoutUtils.h" +#include "dbLayerMapping.h" +#include "dbCellMapping.h" +#include "dbReader.h" +#include "tlString.h" +#include "utHead.h" + +unsigned int find_layer (const db::Layout &l, int ly, int dt) +{ + for (db::Layout::layer_iterator li = l.begin_layers (); li != l.end_layers (); ++li) { + if ((*li).second->log_equal (db::LayerProperties (ly, dt))) { + return (*li).first; + } + } + tl_assert (false); +} + +TEST(1) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + db::LayerMapping lm; + lm.create (l2, l1); + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + EXPECT_EQ (lm.has_mapping (li1), true); + EXPECT_EQ (lm.layer_mapping_pair (li1).first, true); + EXPECT_EQ (l2.get_properties (lm.layer_mapping_pair (li1).second).to_string (), "1/0"); + EXPECT_EQ (lm.has_mapping (li2), true); + EXPECT_EQ (lm.layer_mapping_pair (li2).first, true); + EXPECT_EQ (l2.get_properties (lm.layer_mapping_pair (li2).second).to_string (), "2/0"); + EXPECT_EQ (lm.has_mapping (li3), false); + EXPECT_EQ (lm.layer_mapping_pair (li3).first, false); + + lm.clear (); + EXPECT_EQ (lm.has_mapping (li1), false); + EXPECT_EQ (lm.has_mapping (li2), false); + EXPECT_EQ (lm.has_mapping (li3), false); + + lm.create_full (l2, l1); + + EXPECT_EQ (lm.has_mapping (li1), true); + EXPECT_EQ (lm.layer_mapping_pair (li1).first, true); + EXPECT_EQ (l2.get_properties (lm.layer_mapping_pair (li1).second).to_string (), "1/0"); + EXPECT_EQ (lm.has_mapping (li2), true); + EXPECT_EQ (lm.layer_mapping_pair (li2).first, true); + EXPECT_EQ (l2.get_properties (lm.layer_mapping_pair (li2).second).to_string (), "2/0"); + EXPECT_EQ (lm.has_mapping (li3), true); + EXPECT_EQ (lm.layer_mapping_pair (li3).first, true); + EXPECT_EQ (l2.get_properties (lm.layer_mapping_pair (li3).second).to_string (), "3/0"); +} + +// Tests merge_layout with no specific mapping (plain duplication of the tree) +TEST(2) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + db::LayerMapping lm; + lm.map (li1, l2.insert_layer (db::LayerProperties (11, 0))); + lm.map (li2, l2.insert_layer (db::LayerProperties (12, 0))); + lm.map (li3, l2.insert_layer (db::LayerProperties (13, 0))); + + db::CellMapping cm; + std::map fm; + std::vector src; + src.push_back (l1.cell_by_name ("TOP").second); + db::merge_layouts (l2, l1, db::ICplxTrans (), src, cm.table (), lm.table (), &fm); + + CHECKPOINT(); + compare_layouts (l2, ut::testsrc () + "/testdata/algo/layout_utils_au2.gds"); + + EXPECT_EQ (fm.find (l1.cell_by_name ("TOP").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("TOP").second)->second), "TOP$1"); + EXPECT_EQ (fm.find (l1.cell_by_name ("A").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("A").second)->second), "A$1"); + EXPECT_EQ (fm.find (l1.cell_by_name ("B").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("B").second)->second), "B$1"); + EXPECT_EQ (fm.find (l1.cell_by_name ("C").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("C").second)->second), "C$1"); +} + +// Tests merge_layout with a single mapped cell (the others are mapped automatically) +TEST(3) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + db::LayerMapping lm; + lm.map (li1, l2.insert_layer (db::LayerProperties (11, 0))); + lm.map (li2, l2.insert_layer (db::LayerProperties (12, 0))); + lm.map (li3, l2.insert_layer (db::LayerProperties (13, 0))); + + db::CellMapping cm; + std::map fm; + std::vector src; + src.push_back (l1.cell_by_name ("TOP").second); + cm.map (src.front (), l2.add_cell ("TOPTOP")); + db::merge_layouts (l2, l1, db::ICplxTrans (), src, cm.table (), lm.table (), &fm); + + CHECKPOINT(); + compare_layouts (l2, ut::testsrc () + "/testdata/algo/layout_utils_au3.gds"); + + EXPECT_EQ (fm.find (l1.cell_by_name ("TOP").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("TOP").second)->second), "TOPTOP"); + EXPECT_EQ (fm.find (l1.cell_by_name ("A").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("A").second)->second), "A$1"); + EXPECT_EQ (fm.find (l1.cell_by_name ("B").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("B").second)->second), "B$1"); + EXPECT_EQ (fm.find (l1.cell_by_name ("C").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("C").second)->second), "C$1"); +} + +// Tests merge_layout with a mapped tree (by name) +TEST(4) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + db::LayerMapping lm; + lm.map (li1, l2.insert_layer (db::LayerProperties (11, 0))); + lm.map (li2, l2.insert_layer (db::LayerProperties (12, 0))); + lm.map (li3, l2.insert_layer (db::LayerProperties (13, 0))); + + db::CellMapping cm; + std::vector src; + src.push_back (l1.cell_by_name ("TOP").second); + cm.create_from_names_full (l2, l2.cell_by_name ("TOP").second, l1, src.front ()); + std::map fm; + db::merge_layouts (l2, l1, db::ICplxTrans (), src, cm.table (), lm.table (), &fm); + + CHECKPOINT(); + compare_layouts (l2, ut::testsrc () + "/testdata/algo/layout_utils_au4.gds"); + + EXPECT_EQ (fm.find (l1.cell_by_name ("TOP").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("TOP").second)->second), "TOP"); + EXPECT_EQ (fm.find (l1.cell_by_name ("A").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("A").second)->second), "A"); + EXPECT_EQ (fm.find (l1.cell_by_name ("B").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("B").second)->second), "B"); + EXPECT_EQ (fm.find (l1.cell_by_name ("C").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("C").second)->second), "C"); +} + +// Tests merge_layout with a equivalence-mapped tree +TEST(5) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + db::LayerMapping lm; + lm.map (li1, l2.insert_layer (db::LayerProperties (11, 0))); + lm.map (li2, l2.insert_layer (db::LayerProperties (12, 0))); + lm.map (li3, l2.insert_layer (db::LayerProperties (13, 0))); + + db::CellMapping cm; + std::vector src; + src.push_back (l1.cell_by_name ("TOP").second); + cm.create_from_geometry_full (l2, l2.cell_by_name ("TOP").second, l1, src.front ()); + std::map fm; + db::merge_layouts (l2, l1, db::ICplxTrans (), src, cm.table (), lm.table (), &fm); + + CHECKPOINT(); + compare_layouts (l2, ut::testsrc () + "/testdata/algo/layout_utils_au5.gds"); + + EXPECT_EQ (fm.find (l1.cell_by_name ("TOP").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("TOP").second)->second), "TOP"); + EXPECT_EQ (fm.find (l1.cell_by_name ("A").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("A").second)->second), "A"); + EXPECT_EQ (fm.find (l1.cell_by_name ("B").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("B").second)->second), "B"); + EXPECT_EQ (fm.find (l1.cell_by_name ("C").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("C").second)->second), "C$1"); +} + +// Tests merge_layout with dropping of cell B +TEST(6) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + db::LayerMapping lm; + lm.map (li1, l2.insert_layer (db::LayerProperties (11, 0))); + lm.map (li2, l2.insert_layer (db::LayerProperties (12, 0))); + lm.map (li3, l2.insert_layer (db::LayerProperties (13, 0))); + + db::CellMapping cm; + // Drop cell B + cm.map (l1.cell_by_name ("B").second, db::DropCell); + cm.map (l1.cell_by_name ("TOP").second, l2.cell_by_name ("TOP").second); + + std::map fm; + std::vector src; + src.push_back (l1.cell_by_name ("TOP").second); + db::merge_layouts (l2, l1, db::ICplxTrans (), src, cm.table (), lm.table (), &fm); + + CHECKPOINT(); + compare_layouts (l2, ut::testsrc () + "/testdata/algo/layout_utils_au6.gds"); + + EXPECT_EQ (fm.find (l1.cell_by_name ("TOP").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("TOP").second)->second), "TOP"); + EXPECT_EQ (fm.find (l1.cell_by_name ("A").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("A").second)->second), "A$1"); + EXPECT_EQ (fm.find (l1.cell_by_name ("B").second) != fm.end (), false); + EXPECT_EQ (fm.find (l1.cell_by_name ("C").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("C").second)->second), "C$1"); +} + +// Tests merge_layout with transformation +TEST(7) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l3.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + db::LayerMapping lm; + lm.map (li1, l2.insert_layer (db::LayerProperties (11, 0))); + lm.map (li2, l2.insert_layer (db::LayerProperties (12, 0))); + lm.map (li3, l2.insert_layer (db::LayerProperties (13, 0))); + + db::Layout l2copy = l2; + + db::CellMapping cm; + cm.map (l1.cell_by_name ("TOP").second, l2.cell_by_name ("TOP").second); + + std::map fm; + std::vector src; + src.push_back (l1.cell_by_name ("TOP").second); + db::merge_layouts (l2, l1, db::ICplxTrans (4.0), src, cm.table (), lm.table (), &fm); + + CHECKPOINT(); + compare_layouts (l2, ut::testsrc () + "/testdata/algo/layout_utils_au7.gds"); + + EXPECT_EQ (fm.find (l1.cell_by_name ("TOP").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("TOP").second)->second), "TOP"); + EXPECT_EQ (fm.find (l1.cell_by_name ("A").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("A").second)->second), "A$1"); + EXPECT_EQ (fm.find (l1.cell_by_name ("B").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("B").second)->second), "B$1"); + EXPECT_EQ (fm.find (l1.cell_by_name ("C").second) != fm.end (), true); + EXPECT_EQ (l2.cell_name (fm.find (l1.cell_by_name ("C").second)->second), "C"); + + // Once with final_mapping = 0 ... + db::merge_layouts (l2copy, l1, db::ICplxTrans (4.0), src, cm.table (), lm.table ()); + + CHECKPOINT(); + compare_layouts (l2copy, ut::testsrc () + "/testdata/algo/layout_utils_au7.gds"); +} + +// Tests copy_shapes with no specific mapping (flattening) +TEST(12) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + db::LayerMapping lm; + lm.map (li1, l2.insert_layer (db::LayerProperties (11, 0))); + lm.map (li2, l2.insert_layer (db::LayerProperties (12, 0))); + lm.map (li3, l2.insert_layer (db::LayerProperties (13, 0))); + + db::CellMapping cm; + std::map fm; + std::vector src; + src.push_back (l1.cell_by_name ("TOP").second); + cm.map (src.front (), l2.cell_by_name ("TOP").second); + db::copy_shapes (l2, l1, db::ICplxTrans (), src, cm.table (), lm.table ()); + + CHECKPOINT(); + compare_layouts (l2, ut::testsrc () + "/testdata/algo/layout_utils_au12.gds"); +} + +// Tests copy_shapes with full name mapping +TEST(13) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + db::LayerMapping lm; + lm.map (li1, l2.insert_layer (db::LayerProperties (11, 0))); + lm.map (li2, l2.insert_layer (db::LayerProperties (12, 0))); + lm.map (li3, l2.insert_layer (db::LayerProperties (13, 0))); + + db::CellMapping cm; + std::vector src; + src.push_back (l1.cell_by_name ("TOP").second); + cm.create_from_names_full (l2, l2.cell_by_name ("TOP").second, l1, src.front ()); + db::copy_shapes (l2, l1, db::ICplxTrans (), src, cm.table (), lm.table ()); + + CHECKPOINT(); + compare_layouts (l2, ut::testsrc () + "/testdata/algo/layout_utils_au13.gds"); +} + +// Tests copy_shapes with geo mapping +TEST(14) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + db::LayerMapping lm; + lm.map (li1, l2.insert_layer (db::LayerProperties (11, 0))); + lm.map (li2, l2.insert_layer (db::LayerProperties (12, 0))); + lm.map (li3, l2.insert_layer (db::LayerProperties (13, 0))); + + db::CellMapping cm; + std::vector src; + src.push_back (l1.cell_by_name ("TOP").second); + cm.create_from_geometry_full (l2, l2.cell_by_name ("TOP").second, l1, src.front ()); + db::copy_shapes (l2, l1, db::ICplxTrans (), src, cm.table (), lm.table ()); + + CHECKPOINT(); + compare_layouts (l2, ut::testsrc () + "/testdata/algo/layout_utils_au14.gds"); +} + +// Tests copy_shapes with flattening minus one cell +TEST(15) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + db::LayerMapping lm; + lm.map (li1, l2.insert_layer (db::LayerProperties (11, 0))); + lm.map (li2, l2.insert_layer (db::LayerProperties (12, 0))); + lm.map (li3, l2.insert_layer (db::LayerProperties (13, 0))); + + db::CellMapping cm; + std::vector src; + src.push_back (l1.cell_by_name ("TOP").second); + cm.map (src.front (), l2.cell_by_name ("TOP").second); + cm.map (l1.cell_by_name ("B").second, db::DropCell); + db::copy_shapes (l2, l1, db::ICplxTrans (), src, cm.table (), lm.table ()); + + CHECKPOINT(); + compare_layouts (l2, ut::testsrc () + "/testdata/algo/layout_utils_au15.gds"); +} + +// Tests copy_shapes/move_shapes with no specific mapping (flattening) +TEST(16) +{ + db::Layout l1; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::Layout l2; + { + std::string fn (ut::testsrc ()); + fn += "/testdata/algo/layout_utils_l3.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l2); + } + + unsigned int li1 = find_layer (l1, 1, 0); + unsigned int li2 = find_layer (l1, 2, 0); + unsigned int li3 = find_layer (l1, 3, 0); + + db::LayerMapping lm; + lm.map (li1, l2.insert_layer (db::LayerProperties (11, 0))); + lm.map (li2, l2.insert_layer (db::LayerProperties (12, 0))); + lm.map (li3, l2.insert_layer (db::LayerProperties (13, 0))); + + db::Layout l2copy = l2; + + db::CellMapping cm; + std::map fm; + std::vector src; + src.push_back (l1.cell_by_name ("TOP").second); + cm.map (src.front (), l2.cell_by_name ("TOP").second); + db::copy_shapes (l2, l1, db::ICplxTrans (4.0), src, cm.table (), lm.table ()); + + CHECKPOINT(); + compare_layouts (l2, ut::testsrc () + "/testdata/algo/layout_utils_au16.gds"); + + // ... and one test for move: + db::move_shapes (l2copy, l1, db::ICplxTrans (4.0), src, cm.table (), lm.table ()); + + CHECKPOINT(); + compare_layouts (l2copy, ut::testsrc () + "/testdata/algo/layout_utils_au16.gds"); + compare_layouts (l1, ut::testsrc () + "/testdata/algo/layout_utils_au16b.gds"); + +} + diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 0488daec9..c167b1056 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -31,6 +31,7 @@ SOURCES = \ dbLayerMapping.cc \ dbLayout.cc \ dbLayoutDiff.cc \ + dbLayoutUtils.cc \ dbLayoutQuery.cc \ dbLibraries.cc \ dbMatrix.cc \ diff --git a/testdata/algo/layout_utils_au12.gds b/testdata/algo/layout_utils_au12.gds new file mode 100644 index 000000000..4495e1dbe Binary files /dev/null and b/testdata/algo/layout_utils_au12.gds differ diff --git a/testdata/algo/layout_utils_au13.gds b/testdata/algo/layout_utils_au13.gds new file mode 100644 index 000000000..6cb74050a Binary files /dev/null and b/testdata/algo/layout_utils_au13.gds differ diff --git a/testdata/algo/layout_utils_au14.gds b/testdata/algo/layout_utils_au14.gds new file mode 100644 index 000000000..eb38a66ba Binary files /dev/null and b/testdata/algo/layout_utils_au14.gds differ diff --git a/testdata/algo/layout_utils_au15.gds b/testdata/algo/layout_utils_au15.gds new file mode 100644 index 000000000..6a897097d Binary files /dev/null and b/testdata/algo/layout_utils_au15.gds differ diff --git a/testdata/algo/layout_utils_au16.gds b/testdata/algo/layout_utils_au16.gds new file mode 100644 index 000000000..b27eb1f21 Binary files /dev/null and b/testdata/algo/layout_utils_au16.gds differ diff --git a/testdata/algo/layout_utils_au16b.gds b/testdata/algo/layout_utils_au16b.gds new file mode 100644 index 000000000..3816a5a37 Binary files /dev/null and b/testdata/algo/layout_utils_au16b.gds differ diff --git a/testdata/algo/layout_utils_au2.gds b/testdata/algo/layout_utils_au2.gds new file mode 100644 index 000000000..7cfa8cad0 Binary files /dev/null and b/testdata/algo/layout_utils_au2.gds differ diff --git a/testdata/algo/layout_utils_au3.gds b/testdata/algo/layout_utils_au3.gds new file mode 100644 index 000000000..63692e20b Binary files /dev/null and b/testdata/algo/layout_utils_au3.gds differ diff --git a/testdata/algo/layout_utils_au4.gds b/testdata/algo/layout_utils_au4.gds new file mode 100644 index 000000000..aaa566b9c Binary files /dev/null and b/testdata/algo/layout_utils_au4.gds differ diff --git a/testdata/algo/layout_utils_au5.gds b/testdata/algo/layout_utils_au5.gds new file mode 100644 index 000000000..1d552a92b Binary files /dev/null and b/testdata/algo/layout_utils_au5.gds differ diff --git a/testdata/algo/layout_utils_au6.gds b/testdata/algo/layout_utils_au6.gds new file mode 100644 index 000000000..f33de6983 Binary files /dev/null and b/testdata/algo/layout_utils_au6.gds differ diff --git a/testdata/algo/layout_utils_au7.gds b/testdata/algo/layout_utils_au7.gds new file mode 100644 index 000000000..e5f87462c Binary files /dev/null and b/testdata/algo/layout_utils_au7.gds differ diff --git a/testdata/algo/layout_utils_l1.gds b/testdata/algo/layout_utils_l1.gds new file mode 100644 index 000000000..b38fa80d5 Binary files /dev/null and b/testdata/algo/layout_utils_l1.gds differ diff --git a/testdata/algo/layout_utils_l2.gds b/testdata/algo/layout_utils_l2.gds new file mode 100644 index 000000000..66e9e3212 Binary files /dev/null and b/testdata/algo/layout_utils_l2.gds differ diff --git a/testdata/algo/layout_utils_l3.gds b/testdata/algo/layout_utils_l3.gds new file mode 100644 index 000000000..d5f9d4ce5 Binary files /dev/null and b/testdata/algo/layout_utils_l3.gds differ diff --git a/testdata/ruby/dbCellMapping.rb b/testdata/ruby/dbCellMapping.rb index 3dfb7167a..16e38d824 100644 --- a/testdata/ruby/dbCellMapping.rb +++ b/testdata/ruby/dbCellMapping.rb @@ -28,7 +28,12 @@ def mapping_to_s(ly1, ly2, cm) ly1.each_cell_top_down do |c| s = ly1.cell(c).name if cm.has_mapping?(c) - s += "=>" + ly2.cell(cm.cell_mapping(c)).name + t = cm.cell_mapping(c) + if t == RBA::CellMapping::DropCell + s += "=>(0)" + else + s += "=>" + ly2.cell(t).name + end end r == "" || (r += ";") r += s @@ -50,6 +55,8 @@ class DBCellMapping_TestClass < TestBase assert_equal(mp.has_mapping?(1), false) mp.map(1, 2) assert_equal(mp.cell_mapping(1), 2) + mp.map(1, 3) + assert_equal(mp.cell_mapping(1), 3) ly = RBA::Layout::new @@ -170,6 +177,11 @@ class DBCellMapping_TestClass < TestBase assert_equal(mapping_to_s(ly2, ly1dup, mp), "c0;c2=>c2;c1=>c1;c3=>c3$1") assert_equal(nc.inspect, "[3]") + mp.clear + mp.from_geometry(ly1, top1, ly2, top2) + mp.map(ci2, RBA::CellMapping::DropCell) + assert_equal(mapping_to_s(ly2, ly1, mp), "c0;c2=>(0);c1=>c1;c3") + end end diff --git a/testdata/ruby/dbLayerMapping.rb b/testdata/ruby/dbLayerMapping.rb index c1e353a01..62d70fc96 100644 --- a/testdata/ruby/dbLayerMapping.rb +++ b/testdata/ruby/dbLayerMapping.rb @@ -50,6 +50,8 @@ class DBLayerMapping_TestClass < TestBase assert_equal(mp.has_mapping?(1), false) mp.map(1, 2) assert_equal(mp.layer_mapping(1), 2) + mp.map(1, 3) + assert_equal(mp.layer_mapping(1), 3) ly1 = RBA::Layout::new