diff --git a/src/db/db/dbCellVariants.h b/src/db/db/dbCellVariants.h index ea958bdcd..8a5497c61 100644 --- a/src/db/db/dbCellVariants.h +++ b/src/db/db/dbCellVariants.h @@ -104,16 +104,32 @@ struct DB_PUBLIC GridReducer db::ICplxTrans operator () (const db::ICplxTrans &trans) const { - return db::ICplxTrans (db::Vector (trans.disp ().x () % m_grid, trans.disp ().y () % m_grid)); + // NOTE: we need to keep magnification, angle and mirror so when combining the + // reduced transformations, the result will be equivalent to reducing the combined + // transformation. + db::ICplxTrans res (trans); + res.disp (db::Vector (mod (trans.disp ().x ()), mod (trans.disp ().y ()))); + return res; } db::Trans operator () (const db::Trans &trans) const { - return db::Trans (db::Vector (trans.disp ().x () % m_grid, trans.disp ().y () % m_grid)); + db::Trans res (trans); + res.disp (db::Vector (mod (trans.disp ().x ()), mod (trans.disp ().y ()))); + return res; } private: db::Coord m_grid; + + inline db::Coord mod (db::Coord c) const + { + if (c < 0) { + return m_grid - (-c) % m_grid; + } else { + return c % m_grid; + } + } }; /** @@ -132,7 +148,7 @@ private: * */ template -class DB_PUBLIC cell_variants_builder +class DB_PUBLIC cell_variants_collector { public: typedef cell_variants_reduce_traits compare_traits; @@ -140,19 +156,19 @@ public: /** * @brief Creates a variant extractor */ - cell_variants_builder (const Reduce &red) + cell_variants_collector (const Reduce &red) : m_red (red) { // .. nothing yet .. } /** - * @brief Builds cell variants for the given layout starting from the top cell + * @brief Collects cell variants for the given layout starting from the top cell */ - void build (db::Layout &layout, db::Cell &top_cell) + void collect (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 ()); + // The top cell gets a "variant" with unit transformation + m_variants [top_cell.cell_index ()].insert (std::make_pair (db::ICplxTrans (), 1)); std::set called; top_cell.collect_called_cells (called); @@ -165,17 +181,17 @@ public: // collect the parent variants per parent cell - std::map > 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 ()]; + std::map &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]; + std::map &new_variants = m_variants [*c]; - for (std::map >::const_iterator pv = variants_per_parent_cell.begin (); pv != variants_per_parent_cell.end (); ++pv) { + 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); } @@ -183,12 +199,120 @@ public: } /** - * @brief Gets the variants for a given cell + * @brief Creates cell variants for singularization of the different variants + * + * After this method can been used, all cells with more than one variant are separated and + * the corresponding instances are updated. + * + * If given, *var_table will be filled with a map giving the new cell and variant against + * the old cell for all cells with more than one variant. */ - const std::set &variants (db::cell_index_type ci) const + void separate_variants (db::Layout &layout, db::Cell &top_cell, std::map > *var_table = 0) { - std::map >::const_iterator v = m_variants.find (ci); - static std::set empty_set; + db::LayoutLocker locker (&layout); + + std::set called; + top_cell.collect_called_cells (called); + called.insert (top_cell.cell_index ()); + + // create new cells for the variants + + std::map > var_table_intern; + if (! var_table) { + var_table = &var_table_intern; + } + + for (db::Layout::bottom_up_const_iterator c = layout.begin_bottom_up (); c != layout.end_bottom_up (); ++c) { + + if (called.find (*c) == called.end ()) { + continue; + } + + db::Cell &cell = layout.cell (*c); + + std::map &vv = m_variants [*c]; + if (vv.size () > 1) { + + std::map &vt = (*var_table) [*c]; + + std::vector inst; + inst.reserve (cell.cell_instances ()); + + for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) { + inst.push_back (db::CellInstArrayWithProperties (i->cell_inst (), i->prop_id ())); + } + + cell.clear_insts (); + + int index = 0; + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v, ++index) { + + db::cell_index_type ci_var; + + if (v != vv.begin ()) { + + std::string var_name = layout.cell_name (*c); + var_name += "_VAR" + tl::to_string (index); + + ci_var = layout.add_cell (var_name.c_str ()); + copy_shapes (layout, ci_var, *c); + + // a new entry for the variant + m_variants [ci_var].insert (*v); + + } else { + ci_var = *c; + } + + vt.insert (std::make_pair (v->first, ci_var)); + create_var_instances (layout.cell (ci_var), inst, v->first, *var_table, typename compare_traits::is_translation_invariant ()); + + } + + // correct the first (remaining) entry + std::pair v1 = *vt.begin (); + vv.clear (); + vv.insert (v1); + + } else { + + // if the children of this cell are separated, map the instances to the new variants + bool needs_update = false; + for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end () && ! needs_update; ++cc) { + if (var_table->find (*cc) != var_table->end ()) { + needs_update = true; + } + } + + if (needs_update) { + + std::vector inst; + inst.reserve (cell.cell_instances ()); + + for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) { + inst.push_back (db::CellInstArrayWithProperties (i->cell_inst (), i->prop_id ())); + } + + cell.clear_insts (); + create_var_instances (cell, inst, vv.begin ()->first, *var_table, typename compare_traits::is_translation_invariant ()); + + } + + } + + } + } + + /** + * @brief Gets the variants for a given cell + * + * The keys of the map are the variants, the values is the instance count of the variant + * (as seen from the top cell). + */ + const std::map &variants (db::cell_index_type ci) const + { + std::map >::const_iterator v = m_variants.find (ci); + static std::map empty_set; if (v == m_variants.end ()) { return empty_set; } else { @@ -197,39 +321,104 @@ public: } private: - std::map > m_variants; + std::map > m_variants; Reduce m_red; - void add_variant (std::set &variants, const db::CellInstArray &inst, tl::false_tag) const + void add_variant (std::map &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))); + variants [m_red (inst.complex_trans (*i))] += 1; } } else { for (db::CellInstArray::iterator i = inst.begin (); ! i.at_end (); ++i) { - variants.insert (db::ICplxTrans (m_red (*i))); + variants [db::ICplxTrans (m_red (*i))] += 1; } } } - void add_variant (std::set &variants, const db::CellInstArray &inst, tl::true_tag) const + void add_variant (std::map &variants, const db::CellInstArray &inst, tl::true_tag) const { if (inst.is_complex ()) { - variants.insert (m_red (inst.complex_trans ())); + variants [m_red (inst.complex_trans ())] += inst.size (); } else { - variants.insert (db::ICplxTrans (m_red (inst.front ()))); + variants [db::ICplxTrans (m_red (inst.front ()))] += inst.size (); } } - void product (const std::set &v1, const std::set &v2, std::set &prod) const + void product (const std::map &v1, const std::map &v2, std::map &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)); + for (std::map::const_iterator i = v1.begin (); i != v1.end (); ++i) { + for (std::map::const_iterator j = v2.begin (); j != v2.end (); ++j) { + prod [m_red (i->first * j->first)] += i->second * j->second; } } } + + void copy_shapes (db::Layout &layout, db::cell_index_type ci_to, db::cell_index_type ci_from) const + { + db::Cell &to = layout.cell (ci_to); + const db::Cell &from = layout.cell (ci_from); + for (db::Layout::layer_iterator li = layout.begin_layers (); li != layout.end_layers (); ++li) { + to.shapes ((*li).first) = from.shapes ((*li).first); + } + } + + void create_var_instances (db::Cell &in_cell, std::vector &inst, const db::ICplxTrans &for_var, const std::map > &var_table, tl::false_tag) const + { + for (std::vector::const_iterator i = inst.begin (); i != inst.end (); ++i) { + + std::map >::const_iterator f = var_table.find (i->object ().cell_index ()); + if (f == var_table.end ()) { + + in_cell.insert (*i); + + } else { + + const std::map &vt = f->second; + + for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) { + + db::ICplxTrans rt = m_red (for_var * i->complex_trans (*ia)); + std::map::const_iterator v = vt.find (rt); + tl_assert (v != vt.end ()); + + in_cell.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (v->second), i->complex_trans (*ia)), i->properties_id ())); + + } + + } + + } + } + + void create_var_instances (db::Cell &in_cell, std::vector &inst, const db::ICplxTrans &for_var, const std::map > &var_table, tl::true_tag) const + { + for (std::vector::const_iterator i = inst.begin (); i != inst.end (); ++i) { + + std::map >::const_iterator f = var_table.find (i->object ().cell_index ()); + if (f == var_table.end ()) { + + in_cell.insert (*i); + + } else { + + const std::map &vt = f->second; + + std::map::const_iterator v; + + db::ICplxTrans rt = m_red (for_var * i->complex_trans ()); + v = vt.find (rt); + tl_assert (v != vt.end ()); + + db::CellInstArrayWithProperties new_inst = *i; + new_inst.object ().cell_index (v->second); + in_cell.insert (new_inst); + + } + + } + } }; } // namespace db diff --git a/src/db/unit_tests/dbCellVariantsTests.cc b/src/db/unit_tests/dbCellVariantsTests.cc index a5f635577..52a154bc9 100644 --- a/src/db/unit_tests/dbCellVariantsTests.cc +++ b/src/db/unit_tests/dbCellVariantsTests.cc @@ -22,23 +22,67 @@ #include "dbCellVariants.h" +#include "dbTestSupport.h" +#include "dbReader.h" #include "tlUnitTest.h" +#include "tlStream.h" -std::string var2str (const std::set &vars) +std::string var2str (const std::map &vars) { std::string res; - for (std::set::const_iterator i = vars.begin (); i != vars.end (); ++i) { + for (std::map::const_iterator i = vars.begin (); i != vars.end (); ++i) { if (! res.empty ()) { res += ";"; } - res += i->to_string (); + res += i->first.to_string (); + res += "["; + res += tl::to_string (i->second); + res += "]"; + } + return res; +} + +std::string vm2str (const db::Layout &ly, const std::map > &vm) +{ + std::string res; + for (std::map >::const_iterator i = vm.begin (); i != vm.end (); ++i) { + if (! res.empty ()) { + res += ";"; + } + res += ly.cell_name (i->first); + res += ":"; + for (std::map::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { + if (j != i->second.begin ()) { + res += ","; + } + res += ly.cell_name (j->second); + res += "["; + res += j->first.to_string (); + res += "]"; + } + } + return res; +} + +std::string inst2str (const db::Layout &ly, const db::Cell &cell) +{ + std::string res; + for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) { + for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) { + db::ICplxTrans rt = i->complex_trans (*ia); + if (! res.empty ()) { + res += ";"; + } + res += ly.cell_name (i->cell_index ()); + res += ":"; + res += rt.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")); @@ -48,17 +92,21 @@ TEST(1_Trivial) 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"); + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[1]"); EXPECT_EQ (var2str (vb.variants (c.cell_index ())), ""); EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); + + std::map > vm; + vb.separate_variants (ly, a, &vm); + EXPECT_EQ (vm.empty (), true); + EXPECT_EQ (vm2str (ly, vm), ""); } TEST(2_TwoVariants) { - db::Layout ly; db::Cell &a = ly.cell (ly.add_cell ("A")); db::Cell &b = ly.cell (ly.add_cell ("B")); @@ -69,17 +117,23 @@ TEST(2_TwoVariants) 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"); + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "m0 *1 0,0[1];r0 *1 0,0[1]"); EXPECT_EQ (var2str (vb.variants (c.cell_index ())), ""); EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B:m0 *1 1,100"); + + std::map > vm; + vb.separate_variants (ly, a, &vm); + EXPECT_EQ (vm2str (ly, vm), "B:B[m0 *1 0,0],B_VAR1[r0 *1 0,0]"); + EXPECT_EQ (inst2str (ly, a), "B_VAR1:r0 *1 1,10;B:m0 *1 1,100"); } TEST(3_TwoLevels) { - db::Layout ly; db::Cell &a = ly.cell (ly.add_cell ("A")); db::Cell &b = ly.cell (ly.add_cell ("B")); @@ -92,10 +146,249 @@ TEST(3_TwoLevels) 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"); + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[1];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "m0 *1 0,0[1];r0 *1 0,0[1];m45 *1 0,0[1];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B:r90 *1 1,100"); + EXPECT_EQ (inst2str (ly, b), "C:r0 *1 2,10;C:m0 *1 2,100"); + + std::map > vm; + vb.separate_variants (ly, a, &vm); + EXPECT_EQ (vm2str (ly, vm), "B:B[r0 *1 0,0],B_VAR1[r90 *1 0,0];C:C[m0 *1 0,0],C_VAR1[r0 *1 0,0],C_VAR2[m45 *1 0,0],C_VAR3[r90 *1 0,0]"); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B_VAR1:r90 *1 1,100"); + EXPECT_EQ (inst2str (ly, b), "C_VAR1:r0 *1 2,10;C:m0 *1 2,100"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("B_VAR1").second)), "C_VAR3:r0 *1 2,10;C_VAR2:m0 *1 2,100"); +} + +TEST(4_ThreeLevels) +{ + 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)))); + c.insert (db::CellInstArray (db::CellInst (d.cell_index ()), db::Trans (1, true, db::Vector (0, 0)))); + + db::OrientationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[1];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "m0 *1 0,0[1];r0 *1 0,0[1];m45 *1 0,0[1];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), "r270 *1 0,0[1];m90 *1 0,0[1];r0 *1 0,0[1];m45 *1 0,0[1]"); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B:r90 *1 1,100"); + EXPECT_EQ (inst2str (ly, b), "C:r0 *1 2,10;C:m0 *1 2,100"); + EXPECT_EQ (inst2str (ly, c), "D:m45 *1 0,0"); + + std::map > vm; + vb.separate_variants (ly, a, &vm); + EXPECT_EQ (vm2str (ly, vm), "B:B[r0 *1 0,0],B_VAR1[r90 *1 0,0];C:C[m0 *1 0,0],C_VAR1[r0 *1 0,0],C_VAR2[m45 *1 0,0],C_VAR3[r90 *1 0,0];D:D[r270 *1 0,0],D_VAR1[m90 *1 0,0],D_VAR2[r0 *1 0,0],D_VAR3[m45 *1 0,0]"); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B_VAR1:r90 *1 1,100"); + EXPECT_EQ (inst2str (ly, b), "C_VAR1:r0 *1 2,10;C:m0 *1 2,100"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("B_VAR1").second)), "C_VAR3:r0 *1 2,10;C_VAR2:m0 *1 2,100"); + EXPECT_EQ (inst2str (ly, c), "D:m45 *1 0,0"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("C_VAR1").second)), "D_VAR3:m45 *1 0,0"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("C_VAR2").second)), "D_VAR2:m45 *1 0,0"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("C_VAR3").second)), "D_VAR1:m45 *1 0,0"); +} + +TEST(5_ComplexTrans) +{ + 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::ICplxTrans (db::Trans (0, false, db::Vector (1, 10))))); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::ICplxTrans (db::Trans (1, false, db::Vector (1, 100))))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (db::Trans (0, false, db::Vector (2, 10))))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (db::Trans (0, true, db::Vector (2, 100))))); + + db::OrientationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[1];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "m0 *1 0,0[1];r0 *1 0,0[1];m45 *1 0,0[1];r90 *1 0,0[1]"); EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); } + +TEST(6_Arrays) +{ + + 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::Vector (0, 100), db::Vector (100, 0), 10, 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)), db::Vector (0, 101), db::Vector (101, 0), 10, 10)); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans (0, true, db::Vector (2, 100)))); + + db::OrientationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[100];r90 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "m0 *1 0,0[100];r0 *1 0,0[10000];m45 *1 0,0[1];r90 *1 0,0[100]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); +} + +TEST(7_ScalingVariants) +{ + 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::ICplxTrans (1.5, 0, false, db::Vector (1, 10)), db::Vector (0, 100), db::Vector (100, 0), 10, 10)); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::ICplxTrans (1.0, 90.0, false, db::Vector (1, 100)))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (2.0, 0, false, db::Vector (2, 10)), db::Vector (0, 101), db::Vector (101, 0), 10, 10)); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (1.0, 0, true, db::Vector (2, 100)))); + + db::MagnificationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 0,0[1];r0 *1.5 0,0[100]"); + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "r0 *1 0,0[1];r0 *1.5 0,0[100];r0 *2 0,0[100];r0 *3 0,0[10000]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); +} + +TEST(8_GridVariants) +{ + 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::ICplxTrans (1.0, 0, false, db::Vector (1, 10)), db::Vector (0, 101), db::Vector (102, 0), 2, 2)); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (1.0, 0, false, db::Vector (2, 3)))); + + db::GridReducer red (10); + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *1 1,0[1];r0 *1 3,0[1];r0 *1 1,1[1];r0 *1 3,1[1]"); + + // placements are: + // b in a: r0 *1 x=1,1+102 y=10,10+101 + // c in b: r0 *1 x=2,y=3 + // expanded placements: + // c in a: r0 *2 x=1,1+102 y=10,10+101 x r0 *1 x=2,y=3 + // = (3,13),(105,13),(3,114),(105,114) + // expanded placements mod 10: + // c in a: r0 *2 x=1,1+102 y=10,10+101 x r0 *1 x=2,y=3 + // = (3,3),(5,3),(3,4),(5,4) + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "r0 *1 3,3[1];r0 *1 5,3[1];r0 *1 3,4[1];r0 *1 5,4[1]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B:r0 *1 1,111;B:r0 *1 103,10;B:r0 *1 103,111"); + EXPECT_EQ (inst2str (ly, b), "C:r0 *1 2,3"); + EXPECT_EQ (inst2str (ly, c), ""); + + std::map > vm; + vb.separate_variants (ly, a, &vm); + EXPECT_EQ (vm2str (ly, vm), "B:B[r0 *1 1,0],B_VAR1[r0 *1 3,0],B_VAR2[r0 *1 1,1],B_VAR3[r0 *1 3,1];C:C[r0 *1 3,3],C_VAR1[r0 *1 5,3],C_VAR2[r0 *1 3,4],C_VAR3[r0 *1 5,4]"); + + EXPECT_EQ (inst2str (ly, a), "B:r0 *1 1,10;B_VAR2:r0 *1 1,111;B_VAR1:r0 *1 103,10;B_VAR3:r0 *1 103,111"); + EXPECT_EQ (inst2str (ly, b), "C:r0 *1 2,3"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("B_VAR1").second)), "C_VAR1:r0 *1 2,3"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("B_VAR2").second)), "C_VAR2:r0 *1 2,3"); + EXPECT_EQ (inst2str (ly, ly.cell (ly.cell_by_name ("B_VAR3").second)), "C_VAR3:r0 *1 2,3"); + EXPECT_EQ (inst2str (ly, c), ""); +} + +TEST(9_ComplexGridVariants) +{ + 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::ICplxTrans (2.0, 0, false, db::Vector (1, 10)), db::Vector (0, 101), db::Vector (102, 0), 2, 2)); + a.insert (db::CellInstArray (db::CellInst (b.cell_index ()), db::ICplxTrans (1.0, 90.0, false, db::Vector (1, 100)))); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (2.0, 0, false, db::Vector (2, 10)), db::Vector (0, 103), db::Vector (105, 0), 2, 2)); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::ICplxTrans (1.0, 0, true, db::Vector (2, 100)))); + + db::GridReducer red (10); + db::cell_variants_collector vb (red); + vb.collect (ly, a); + EXPECT_EQ (var2str (vb.variants (a.cell_index ())), "r0 *1 0,0[1]"); + EXPECT_EQ (var2str (vb.variants (b.cell_index ())), "r0 *2 1,0[1];r90 *1 1,0[1];r0 *2 3,0[1];r0 *2 1,1[1];r0 *2 3,1[1]"); + + // placements are: + // b in a: r0 *2 x=1,1+102 y=10,10+101 + // r90 *1 x=1,y=100 + // c in b: r0 *2 x=2,2+105 y=10,10+103 + // m0 *1 x=2,y=100 + // expanded placements: + // c in a: r0 *2 x=1,1+102 y=10,10+101 x r0 *2 x=2,2+105 y=10,10+103 + // = (5,30),(215,30),(5,236),(215,236) + // (107,30),(317,30),(107,236),(317,236) + // (5,131),(215,131),(5,337),(215,337) + // (107,131),(317,131),(107,337),(317,337) + // r0 *2 x=1,1+102 y=10,10+101 x m0 *1 x=2,y=100 + // (5,210),(5,311),(107,210),(107,311) + // r90 *1 x=1,y=100 x r0 *2 x=2,2+105 y=10,10+103 + // (-9,102),(-9,207),(-112,102),(-112,207) + // r90 *1 x=1,y=100 x m0 *1 x=2,y=100 + // (-99,102) + // expanded placements mod 10: + // c in a: r0 *2 x=1,1+102 y=10,10+101 x r0 *2 x=2,2+105 y=10,10+103 + // = (5,0),(5,0),(5,6),(5,6) + // (7,0),(7,0),(7,6),(7,6) + // (5,1),(5,1),(5,7),(5,7) + // (7,1),(7,1),(7,7),(7,7) + // r0 *2 x=1,1+102 y=10,10+101 x m0 *1 x=2,y=100 + // (5,0),(5,1),(7,0),(7,1) + // r90 *1 x=1,y=100 x r0 *2 x=2,2+105 y=10,10+103 + // (1,2),(1,7),(8,2),(8,7) + // r90 *1 x=1,y=100 x m0 *1 x=2,y=100 + // (1,2) + EXPECT_EQ (var2str (vb.variants (c.cell_index ())), "m0 *2 5,0[1];r0 *4 5,0[2];m0 *2 7,0[1];r0 *4 7,0[2];m0 *2 5,1[1];r0 *4 5,1[2];" + "m0 *2 7,1[1];r0 *4 7,1[2];m45 *1 1,2[1];r90 *2 1,2[1];r90 *2 8,2[1];" + "r0 *4 5,6[2];r0 *4 7,6[2];r90 *2 1,7[1];r0 *4 5,7[2];r0 *4 7,7[2];r90 *2 8,7[1]"); + EXPECT_EQ (var2str (vb.variants (d.cell_index ())), ""); +} + +TEST(100_OrientationVariantsWithLayout) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/deep_region_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + db::OrientationReducer red; + db::cell_variants_collector vb (red); + vb.collect (ly, top_cell); + vb.separate_variants (ly, top_cell); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/cell_variants_au1.gds"); +} diff --git a/testdata/algo/cell_variants_au1.gds b/testdata/algo/cell_variants_au1.gds new file mode 100644 index 000000000..3234662e5 Binary files /dev/null and b/testdata/algo/cell_variants_au1.gds differ