diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 53e212822..c87a0ee0d 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -166,7 +166,8 @@ SOURCES = \ dbLocalOperationUtils.cc \ gsiDeclDbDeepShapeStore.cc \ dbNetlistSpiceWriter.cc \ - dbNetlistWriter.cc + dbNetlistWriter.cc \ + dbCellVariants.cc HEADERS = \ dbArray.h \ @@ -297,7 +298,8 @@ HEADERS = \ dbLocalOperationUtils.h \ dbDeepRegion.h \ dbNetlistSpiceWriter.h \ - dbNetlistWriter.h + dbNetlistWriter.h \ + dbCellVariants.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc new file mode 100644 index 000000000..0222108cd --- /dev/null +++ b/src/db/db/dbCellVariants.cc @@ -0,0 +1,32 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbCellVariants.h" + +namespace db +{ + + // .. nothing yet .. + +} + diff --git a/src/db/db/dbCellVariants.h b/src/db/db/dbCellVariants.h new file mode 100644 index 000000000..ea958bdcd --- /dev/null +++ b/src/db/db/dbCellVariants.h @@ -0,0 +1,238 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_dbCellVariants +#define HDR_dbCellVariants + +#include "dbCommon.h" + +#include "dbLayout.h" +#include "dbTrans.h" +#include "tlTypeTraits.h" + +namespace db +{ + +/** + * @brief Tells some properties of a reduce function for cell_variants_builder + */ +template +struct DB_PUBLIC cell_variants_reduce_traits +{ + /** + * @brief Indicates whether the result of the compare function does not depend on translation + */ + typedef typename T::is_translation_invariant is_translation_invariant; +}; + +/** + * @brief An orientation reducer + * + * This reducer incarnation reduces the transformation to it's rotation/mirror part. + */ +struct DB_PUBLIC OrientationReducer +{ + typedef tl::true_tag is_translation_invariant; + + db::ICplxTrans operator () (const db::ICplxTrans &trans) const + { + db::ICplxTrans res (trans); + res.disp (db::Vector ()); + res.mag (1.0); + return res; + } + + db::Trans operator () (const db::Trans &trans) const + { + return db::Trans (trans.fp_trans ()); + } +}; + +/** + * @brief A magnification reducer + * + * This reducer incarnation reduces the transformation to it's scaling part. + */ +struct DB_PUBLIC MagnificationReducer +{ + typedef tl::true_tag is_translation_invariant; + + db::ICplxTrans operator () (const db::ICplxTrans &trans) const + { + return db::ICplxTrans (trans.mag ()); + } + + db::Trans operator () (const db::Trans &) const + { + return db::Trans (); + } +}; + +/** + * @brief A grid reducer + * + * This reducer incarnation reduces the transformation to it's displacement modulo a grid + */ +struct DB_PUBLIC GridReducer +{ + typedef tl::false_tag is_translation_invariant; + + GridReducer (db::Coord grid) + : m_grid (grid) + { + // .. nothing yet .. + } + + db::ICplxTrans operator () (const db::ICplxTrans &trans) const + { + return db::ICplxTrans (db::Vector (trans.disp ().x () % m_grid, trans.disp ().y () % m_grid)); + } + + db::Trans operator () (const db::Trans &trans) const + { + return db::Trans (db::Vector (trans.disp ().x () % m_grid, trans.disp ().y () % m_grid)); + } + +private: + db::Coord m_grid; +}; + +/** + * @brief A class computing variants for cells according to a given criterion + * + * The cell variants are build from the cell instances and are accumulated over + * the hierarchy path. + * + * The criterion is a reduction function which defines the core information of a + * db::ICplxTrans or db::Trans object to be used for the comparison. + * + * In any case, the compare function is expected to be distributive similar to + * the modulo function (A, B are transformations) + * + * RED(A*B) = RED(RED(A)*RED(B)) + * + */ +template +class DB_PUBLIC cell_variants_builder +{ +public: + typedef cell_variants_reduce_traits compare_traits; + + /** + * @brief Creates a variant extractor + */ + cell_variants_builder (const Reduce &red) + : m_red (red) + { + // .. nothing yet .. + } + + /** + * @brief Builds cell variants for the given layout starting from the top cell + */ + void build (db::Layout &layout, db::Cell &top_cell) + { + // The top cell gets a single "variant" with unit transformation + m_variants [top_cell.cell_index ()].insert (db::ICplxTrans ()); + + std::set called; + top_cell.collect_called_cells (called); + + for (db::Layout::top_down_const_iterator c = layout.begin_top_down (); c != layout.end_top_down (); ++c) { + + if (called.find (*c) == called.end ()) { + continue; + } + + // collect the parent variants per parent cell + + std::map > variants_per_parent_cell; + for (db::Cell::parent_inst_iterator pi = layout.cell (*c).begin_parent_insts (); ! pi.at_end (); ++pi) { + std::set &variants = variants_per_parent_cell [pi->inst ().object ().cell_index ()]; + add_variant (variants, pi->child_inst ().cell_inst (), typename compare_traits::is_translation_invariant ()); + } + + // compute the resulting variants + + std::set &new_variants = m_variants [*c]; + + for (std::map >::const_iterator pv = variants_per_parent_cell.begin (); pv != variants_per_parent_cell.end (); ++pv) { + product (variants (pv->first), pv->second, new_variants); + } + + } + } + + /** + * @brief Gets the variants for a given cell + */ + const std::set &variants (db::cell_index_type ci) const + { + std::map >::const_iterator v = m_variants.find (ci); + static std::set empty_set; + if (v == m_variants.end ()) { + return empty_set; + } else { + return v->second; + } + } + +private: + std::map > m_variants; + Reduce m_red; + + void add_variant (std::set &variants, const db::CellInstArray &inst, tl::false_tag) const + { + if (inst.is_complex ()) { + for (db::CellInstArray::iterator i = inst.begin (); ! i.at_end (); ++i) { + variants.insert (m_red (inst.complex_trans (*i))); + } + } else { + for (db::CellInstArray::iterator i = inst.begin (); ! i.at_end (); ++i) { + variants.insert (db::ICplxTrans (m_red (*i))); + } + } + } + + void add_variant (std::set &variants, const db::CellInstArray &inst, tl::true_tag) const + { + if (inst.is_complex ()) { + variants.insert (m_red (inst.complex_trans ())); + } else { + variants.insert (db::ICplxTrans (m_red (inst.front ()))); + } + } + + void product (const std::set &v1, const std::set &v2, std::set &prod) const + { + for (std::set::const_iterator i = v1.begin (); i != v1.end (); ++i) { + for (std::set::const_iterator j = v2.begin (); j != v2.end (); ++j) { + prod.insert (m_red (*i * *j)); + } + } + } +}; + +} // namespace db + +#endif + diff --git a/src/db/unit_tests/dbCellVariantsTests.cc b/src/db/unit_tests/dbCellVariantsTests.cc new file mode 100644 index 000000000..a5f635577 --- /dev/null +++ b/src/db/unit_tests/dbCellVariantsTests.cc @@ -0,0 +1,101 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbCellVariants.h" +#include "tlUnitTest.h" + +std::string var2str (const std::set &vars) +{ + std::string res; + for (std::set::const_iterator i = vars.begin (); i != vars.end (); ++i) { + if (! res.empty ()) { + res += ";"; + } + res += i->to_string (); + } + return res; +} + +TEST(1_Trivial) +{ + + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (0, false, db::Vector (1, 10)))); + + db::OrientationReducer red; + db::cell_variants_builder vb (red); + vb.build (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), ""); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); +} + +TEST(2_TwoVariants) +{ + + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (0, false, db::Vector (1, 10)))); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (0, true, db::Vector (1, 100)))); + + db::OrientationReducer red; + db::cell_variants_builder vb (red); + vb.build (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "m0 *1 0,0;r0 *1 0,0"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), ""); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); +} + +TEST(3_TwoLevels) +{ + + db::Layout ly; + db::Cell &a = ly.cell (ly.add_cell ("A")); + db::Cell &b = ly.cell (ly.add_cell ("B")); + db::Cell &c = ly.cell (ly.add_cell ("C")); + db::Cell &d = ly.cell (ly.add_cell ("D")); + + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (0, false, db::Vector (1, 10)))); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::Trans (1, false, db::Vector (1, 100)))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans (0, false, db::Vector (2, 10)))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans (0, true, db::Vector (2, 100)))); + + db::OrientationReducer red; + db::cell_variants_builder vb (red); + vb.build (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0;r90 *1 0,0"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "m0 *1 0,0;r0 *1 0,0;m45 *1 0,0;r90 *1 0,0"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index df7405b8c..c0df3d523 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -66,7 +66,8 @@ SOURCES = \ dbLayoutToNetlistTests.cc \ dbLayoutToNetlistWriterTests.cc \ dbLayoutToNetlistReaderTests.cc \ - dbNetlistWriterTests.cc + dbNetlistWriterTests.cc \ + dbCellVariantsTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC