From a7bfaac424b9153a6bcc240b5bf18c16187884c5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 17 Feb 2019 10:59:04 +0100 Subject: [PATCH] Cell variant resolution by propagation, grid check now implementation hierarchically (with propagation) --- src/db/db/dbCellVariants.h | 131 +++++++++++++++++++++++ src/db/db/dbDeepRegion.cc | 75 ++++++++++++- src/db/db/dbDeepShapeStore.h | 35 ++++++ src/db/unit_tests/dbCellVariantsTests.cc | 44 ++++++++ src/db/unit_tests/dbDeepRegionTests.cc | 37 ++++++- testdata/algo/cell_variants_au2.gds | Bin 0 -> 2032 bytes testdata/algo/cell_variants_l1.gds | Bin 0 -> 1034 bytes testdata/algo/deep_region_au14b.gds | Bin 0 -> 1666 bytes testdata/algo/deep_region_au17.gds | Bin 0 -> 2366 bytes testdata/algo/deep_region_au18.gds | Bin 0 -> 8846 bytes testdata/algo/deep_region_au19.gds | Bin 0 -> 13978 bytes 11 files changed, 319 insertions(+), 3 deletions(-) create mode 100644 testdata/algo/cell_variants_au2.gds create mode 100644 testdata/algo/cell_variants_l1.gds create mode 100644 testdata/algo/deep_region_au14b.gds create mode 100644 testdata/algo/deep_region_au17.gds create mode 100644 testdata/algo/deep_region_au18.gds create mode 100644 testdata/algo/deep_region_au19.gds diff --git a/src/db/db/dbCellVariants.h b/src/db/db/dbCellVariants.h index 11f2fde8d..8e6210ceb 100644 --- a/src/db/db/dbCellVariants.h +++ b/src/db/db/dbCellVariants.h @@ -28,6 +28,7 @@ #include "dbLayout.h" #include "dbTrans.h" #include "tlTypeTraits.h" +#include "tlUtils.h" namespace db { @@ -354,6 +355,123 @@ public: } } + /** + * @brief Commits the shapes for different variants to the current cell hierarchy + * + * This is an alternative approach and will push the variant shapes into the parent hierarchy. + * "to_commit" initially is a set of shapes to commit for the given cell and variant. + * This map is modified during the algorithm and should be discarded later. + */ + virtual void commit_shapes (db::Layout &layout, db::Cell &top_cell, unsigned int layer, std::map > &to_commit) + { + if (to_commit.empty ()) { + return; + } + + // NOTE: this implementation suffers from accumulation of propagated shapes: we add more levels of propagated + // shapes if required. We don't clean up, because we do not know when a shape collection stops being required. + + db::LayoutLocker locker (&layout); + + std::set called; + top_cell.collect_called_cells (called); + called.insert (top_cell.cell_index ()); + + 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 &vvc = m_variants [*c]; + if (vvc.size () > 1) { + + for (std::map::const_iterator vc = vvc.begin (); vc != vvc.end (); ++vc) { + + for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) { + + std::map >::const_iterator tc = to_commit.find (i->cell_index ()); + if (tc != to_commit.end ()) { + + const std::map &vt = tc->second; + + // NOTE: this will add one more commit slot for propagation ... but we don't clean up. + // When would a cleanup happen? + std::map &propagated = to_commit [*c]; + + for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) { + + db::ICplxTrans t = i->complex_trans (*ia); + db::ICplxTrans rt = m_red (vc->first * t); + std::map::const_iterator v = vt.find (rt); + if (v != vt.end ()) { + + db::Shapes &ps = propagated [vc->first]; + tl::ident_map pm; + + for (db::Shapes::shape_iterator si = v->second.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + ps.insert (*si, t, pm); + } + + } + + } + + } + + } + + } + + } else { + + // single variant -> we can commit any shapes we have kept for this cell directly to the cell + + std::map >::iterator l = to_commit.find (*c); + if (l != to_commit.end ()) { + tl_assert (l->second.size () == 1); + cell.shapes (layer).insert (l->second.begin ()->second); + to_commit.erase (l); + } + + // for child cells, pull everything that needs to be committed to the parent + + for (db::Cell::const_iterator i = cell.begin (); ! i.at_end (); ++i) { + + std::map >::const_iterator tc = to_commit.find (i->cell_index ()); + if (tc != to_commit.end ()) { + + const std::map &vt = tc->second; + + for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) { + + db::ICplxTrans t = i->complex_trans (*ia); + db::ICplxTrans rt = m_red (vvc.begin ()->first * t); + std::map::const_iterator v = vt.find (rt); + + if (v != vt.end ()) { + + tl::ident_map pm; + + for (db::Shapes::shape_iterator si = v->second.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + cell.shapes (layer).insert (*si, t, pm); + } + + } + + } + + } + + } + + } + + } + } + /** * @brief Gets the variants for a given cell * @@ -371,6 +489,19 @@ public: } } + /** + * @brief Returns true, if variants have been built + */ + bool has_variants () const + { + for (std::map >::const_iterator i = m_variants.begin (); i != m_variants.end (); ++i) { + if (i->second.size () > 1) { + return true; + } + } + return false; + } + private: std::map > m_variants; Reduce m_red; diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 3fe83e3c6..7ad2960b7 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -713,11 +713,82 @@ DeepRegion::to_string (size_t nmax) const return db::AsIfFlatRegion::to_string (nmax); } +static void produce_offgrid_markers (db::Shapes &markers, const db::Shapes &shapes, const db::ICplxTrans &tr, db::Coord g) +{ + for (db::Shapes::shape_iterator si = shapes.begin (db::ShapeIterator::All); ! si.at_end (); ++si) { + + db::Polygon poly; + si->polygon (poly); + + for (size_t i = 0; i < poly.holes () + 1; ++i) { + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = poly.begin_hull (); + e = poly.end_hull (); + } else { + b = poly.begin_hole ((unsigned int) (i - 1)); + e = poly.end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator p = b; p != e; ++p) { + db::Point pt = tr * *p; + if ((pt.x () % g) != 0 || (pt.y () % g) != 0) { + markers.insert (EdgePair (db::Edge (pt, pt), db::Edge (pt, pt))); + } + } + + } + + } + +} + EdgePairs DeepRegion::grid_check (db::Coord gx, db::Coord gy) const { - // TODO: snap be optimized by forming grid variants etc. - return db::AsIfFlatRegion::grid_check (gx, gy); + if (gx <= 0 || gy <= 0) { + throw tl::Exception (tl::to_string (tr ("Grid check requires a positive grid value"))); + } + + if (gx != gy) { + // no way doing this hierarchically ? + return db::AsIfFlatRegion::grid_check (gx, gy); + } + + ensure_merged_polygons_valid (); + + db::Layout &layout = m_merged_polygons.layout (); + + db::cell_variants_collector vars (gx); + vars.collect (layout, m_merged_polygons.initial_cell ()); + + std::map > to_commit; + std::auto_ptr res (new db::DeepEdgePairs (m_merged_polygons.derived ())); + + for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { + + const std::map &vv = vars.variants (c->cell_index ()); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + + db::Shapes *markers; + if (vv.size () == 1) { + markers = & c->shapes (res->deep_layer ().layer ()); + } else { + markers = & to_commit [c->cell_index ()] [v->first]; + } + + produce_offgrid_markers (*markers, c->shapes (m_merged_polygons.layer ()), v->first, gx); + + } + + } + + // propagate the markers with a similar algorithm used for producing the variants + res->deep_layer ().commit_shapes (vars, to_commit); + + return db::EdgePairs (res.release ()); } EdgePairs diff --git a/src/db/db/dbDeepShapeStore.h b/src/db/db/dbDeepShapeStore.h index 96dc17af3..b76d44510 100644 --- a/src/db/db/dbDeepShapeStore.h +++ b/src/db/db/dbDeepShapeStore.h @@ -159,6 +159,19 @@ public: template void separate_variants (VarCollector &collector); + /** + * @brief Commits shapes for variants to the existing cell hierarchy + * + * The "to_propagate" collection is a set of shapes per cell and variant. The + * algorithm will put these shapes into the existing hierarchy putting the + * shapes into the proper parent cells to resolve variants. + * + * This map will be modified by the algorithm and should be discarded + * later. + */ + template + void commit_shapes (VarCollector &collector, std::map > &to_propagate); + /** * @brief Gets the shape store object * This is a pure const version to prevent manipulation of the store. @@ -299,6 +312,21 @@ public: issue_variants (layout_index, var_map); } + /** + * @brief Commits shapes for variants to the existing cell hierarchy + * + * To use this method, first create a variant collector (db::cell_variant_collector) with the required + * reducer and collect the variants. Then call this method on the desired layout index to commit the shapes for the + * respective variants. + */ + template + void commit_shapes (unsigned int layout_index, VarCollector &coll, unsigned int layer, std::map > &to_commit) + { + tl_assert (is_valid_layout_index (layout_index)); + + coll.commit_shapes (layout (layout_index), initial_cell (layout_index), layer, to_commit); + } + /** * @brief For testing */ @@ -538,6 +566,13 @@ void DeepLayer::separate_variants (VarCollector &collector) mp_store->separate_variants (m_layout, collector); } +template +void DeepLayer::commit_shapes (VarCollector &collector, std::map > &to_commit) +{ + check_dss (); + mp_store->commit_shapes (m_layout, collector, layer (), to_commit); +} + } namespace tl diff --git a/src/db/unit_tests/dbCellVariantsTests.cc b/src/db/unit_tests/dbCellVariantsTests.cc index 8b207a252..f43cd7f1e 100644 --- a/src/db/unit_tests/dbCellVariantsTests.cc +++ b/src/db/unit_tests/dbCellVariantsTests.cc @@ -392,3 +392,47 @@ TEST(100_OrientationVariantsWithLayout) CHECKPOINT(); db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/cell_variants_au1.gds"); } + +TEST(101_Propagation) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/cell_variants_l1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + std::map > to_commit; + + db::cell_index_type top_cell_index = *ly.begin_top_down (); + db::Cell &top_cell = ly.cell (top_cell_index); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.insert_layer (db::LayerProperties (2, 0)); + + db::cell_variants_collector vb; + vb.collect (ly, top_cell); + + for (db::Layout::const_iterator c = ly.begin (); c != ly.end (); ++c) { + + const std::map &vv = vb.variants (c->cell_index ()); + for (std::map::const_iterator v = vv.begin (); v != vv.end (); ++v) { + + db::Shapes &out = to_commit [c->cell_index ()][v->first]; + for (db::Shapes::shape_iterator s = c->shapes (l1).begin (db::ShapeIterator::All); ! s.at_end (); ++s) { + db::Box b = s->bbox ().transformed (v->first); + b.enlarge (db::Vector (-100, 0)); + out.insert (b.transformed (v->first.inverted ())); + } + + } + + } + + vb.commit_shapes (ly, top_cell, l2, to_commit); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/cell_variants_au2.gds"); +} diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 7597be1b2..16bb37458 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -969,7 +969,7 @@ TEST(17_SinglePolygonChecks) } } -TEST(18_Checks) +TEST(18_MultiPolygonChecks) { db::Layout ly; { @@ -1013,6 +1013,41 @@ TEST(18_Checks) } } +TEST(19_GridCheck) +{ + 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::DeepShapeStore dss; + + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss); + db::Region r3_gc1; + r3.grid_check (25, 25).polygons (r3_gc1, 100); + db::Region r3_gc2; + r3.grid_check (40, 40).polygons (r3_gc2, 100); + + db::Layout target; + unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index)); + + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r3); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r3_gc1); + target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r3_gc2); + + CHECKPOINT(); + db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au19.gds"); +} + TEST(100_Integration) { db::Layout ly; diff --git a/testdata/algo/cell_variants_au2.gds b/testdata/algo/cell_variants_au2.gds new file mode 100644 index 0000000000000000000000000000000000000000..c30651bb07912aa49ab3156f014fe2022dd56542 GIT binary patch literal 2032 zcma)+J#Q015J1Q4^ZH~=J}{D{#1at{p+KaGfJ6vkLldxoA_YoK0TKlT1wViS{s9tA zy0nxOl!%%lKOhnf1qB6Vip<;HVb3V*aFYG(nSDDmcc&FE&OMb;%XNQ{;B@AejBpnBV-(U$ z`XBZqESiKbd4wzUXuRxgh?qpJ{Pr z(O><-DiKJ(JU^qwm2-X8<5M5sXmMrH=eZ4EX$9q_yYc=REv}sF^Pbs0+%rq$l>K7OM2x);taJsbX>YTF-T2l1@X%&BL^ zqcyFUuV&9P=P+l=gIn=zdGSW%#wQV@#g#>0y9qwT!!hzZ<%>S`Spz4}V|}B=l|`R5 z_Q9j3(c=I3GiTQN>omVI%stIyuas}rpYgYk)BH;Oox{v9f6Nzs_NPx6=GOW~i_hzy S`mBR^)Hhn(e0}GtN=^W!+QHcX literal 0 HcmV?d00001 diff --git a/testdata/algo/cell_variants_l1.gds b/testdata/algo/cell_variants_l1.gds new file mode 100644 index 0000000000000000000000000000000000000000..56c817fbc77423c547f3e5dc260ca1514929ff96 GIT binary patch literal 1034 zcmbVLJ5Iwu5Pe?TYbzLV2o<=9L;(c~9RUhN3=JOvMGBM?zzsM8+yIFt9W5m%pymij zG?W~Hd0vlk5Qd1A*3X{VH#6_83=3l}ka;G0gFzcDr0CV(5gU7Qm;;M`xI4J*A3r~Q zY#%&rjPFN7WMMPUS~~!O7_hSj9_*X}u%7zztgYw;@ZSN&S}JG@Kf+&KWVWCsT^88S zE_qTRSq=wjSt-fFJuRBlx);@FzVWg!AC-sWjuA&ZWaP|`U6ja4%kQGbPYS7sOO=tM zTjPRLD%_WoZc@?uyTXa7*@OpJSLfJ7y{=0`9IIR?9HUy&UWtH1kMS`{KJXUF6ygM5gj|)a?V{{1DlV4qQ(Uq`7&$4 oCz3~wBHzn=BcJ_whL~Tv{)!qGY~=HdZt~buQRB*&KP(9F3B2@G#{d8T literal 0 HcmV?d00001 diff --git a/testdata/algo/deep_region_au14b.gds b/testdata/algo/deep_region_au14b.gds new file mode 100644 index 0000000000000000000000000000000000000000..85b83f54e5e979436fa4008a55b8d82627165a99 GIT binary patch literal 1666 zcmb7EJxd%>6g~T9_RTn1KUok&Bmofu3kD+u3quToE-ss}S%Sz`skgAOw6L&jqs-#C6BTo-!(XvyE57)-MyXR&<^px z0L6&%Es|DIeF#0-I(Jo}o3|L*hrZEQMO|mSLw`XvERvouJd|&vsO!OEPG9QwMvYVN zJ-^hJ^A*)ck^Q(nL2rHP-{w3Gc~Wnsx!sp%iL&K96*Wi2G*99M)i=f7e205`-_;~f zM)t)?hMc$XxqSNc>@WMce%1@>dUpOjy-!oxsMgosNq@%^G6`wtk}p5n`#COo@8`-# zer~4Y=bpdub1(B#l&fX=!mnF@;pZBAey;U1J<5B8Eddlo}`I*$Q;K=XCsAxAntpg8L{okFcqWEEFiApO}RW}!% MtVicUof(F>1u(-z8~^|S literal 0 HcmV?d00001 diff --git a/testdata/algo/deep_region_au17.gds b/testdata/algo/deep_region_au17.gds new file mode 100644 index 0000000000000000000000000000000000000000..df3ab0872d4acf6d22ccb9d3cdb5320d0d872648 GIT binary patch literal 2366 zcmbVNzl#$=6n>fAOg5{D@k}L$sMsh$MUN8;#ghXKN|YExLjD8`i?dTW(8lfxyR*3J zZ2SW(Qi???3rmrJ^+Dh&<&yDxGkNT8%xr>z@GbA{H{ZOUOPZ8=Mymx?`bCOrRHjRG zIsHtL(hr+=iRwY|_Qty#559gJ-Q4_qWpB6Jq1B?F7i-;VZ?v8eg%OdiC<;hd6%jRR zfidQdF-Jp2qTnp7uLZ@7e* zX#cQ&AoGaSrl8mpeu;7`9ckTcJ-tR@Ocbw3)sJ(PCoiCc3UU?ad*jg@uRdMaCc+80 z(~jqanRn!zdA&A1M1C}Kbx0I(&m0JfoiUTERIlO=r>q~yJmjh(DE53l=QvnH9Ki5{ z%eA1`f$3gP=^OLAhXLw1EOFhDRY9@qPKGbF?r{$7eAOLtt%Fal){NkhdY)i;3;yp! zf~7uD5nMsS^Ypy*eMQgC_E7_O>|uTzd=B#dUQq0ebMNc;Lujno`Ja<7XP11H-EN7e zv!Tyno+&zksPU_F4|4T+({S}AON&|DBYGG2Jg|B;*2OH=;L0#oYc9gYEY_;lSPSfz z%duQnVnf=lTs9l7ce&?OW%;nS-_N~rZ1h19M;T?TGoPnj4yAy*}C`0?c8X^ZD+ep?NlWDIOB^STd!5X zuFm-Cdw$=Uk>7Xmj^B5E$M3r}^!x5#_50e}eqZlf;+uXYGFxkQ%l`S|W|?tz%c25d GNPhw0^1n9# literal 0 HcmV?d00001 diff --git a/testdata/algo/deep_region_au18.gds b/testdata/algo/deep_region_au18.gds new file mode 100644 index 0000000000000000000000000000000000000000..35edb6a153d7b2c7ffe737eaacc1dc6728ea2c35 GIT binary patch literal 8846 zcmcJU!E0Pq6vj_xCNGo8B*q$wlu~Muf;gaPZ4s;*DQMCdC59p){SQKs;!>fY8$pYd z(v16QO2?MAk=@Q%n_bNtQuuP$8v?T5YBFa7e;-S5^`<>YwW zu9Ib}EAz`AiL}}x*>ppilFc+kq&M5l^ZbiE-`}N*G*4snb5k3qO6%#qeXDGL*vRw4 zZTyAi_n?3F)y5CEQ}$r4(OzWiJnj2xv#pxngC3HvYU6P}?Z{UhJ5M|O zE!4&<{H*N%!4~*m=J~-E>^=kgc~cvw4$Qw~5B5%Y4yBz)-3xr#?W?d%=#G&?bNLj=EoIei=Xg~~hjJ4fZ(!6U>cL#!T& zw3#yxO>LYy5UWzIVh+!Q=TO=qR@0_7p0;OWz0Meh{WYh`wQl~XYfXinG&cG=`6-_NA!0hQB{B|MQ^CKB-z(48 zcwukv_b|tG?B5=L2XVh`YU9)+=d1R+V4Ms4|A=_$xW#KyYfGot8`*Q%&(eMhUgOVP z$U&@jYZFAM*9x{f;cZ*9;A_E}O0#BW$qKV{StVI>X;$~F`Zcwv(JW?|u*z?VOf4c` zq204yEAAXxQEON&^KDo`{WBzE@`18Ccib}sqjN`DlMmchV6obhiRxQkuS5*#56=v$ z)#zc7XB7!=2lcXGaV^X&sF$TV!Duk&{n21fF!EGyDFC*JUAH13DDg$ip| z&{@v95@7rwsm zwdG_rZ)(3mt#l4i*5h0ce$~r)J1Z+EY?3uvZm>IHj{L9RNV4!YzHqr-b@DJULJv^s zZKABl46wpJbFNpS2N=S#8dWb3E$98YF>3AA_2OAsFNVzd`ui)=V@G3Xea0x~vh^F4 z{xHho9u7HSHQ1RO&6}_)YHy|cO0bGtubqeeoUeQZBv`Cb;RZU5-I$1T8(;s zF%Nlu)yqSxQFZq+J99n9ycL_bZLCqnEu1$R>JMUA$1k$%O!3xvjUDSeS>8GiE3oV~ zu^ub(4Lc+c&98cArTOLG%cJ|nuTkcq#c;?yI{wFGWnuZ<5A?3QeN1oF!!0;+ntwvB zba1~}e%ARg{%r1U{Mo6!__G(@jz4>K@J4=ME*E#>cJI9$f3|WX{%rmC{Sysz1#gEt6+dfw zU;U!`S>3D3qWW3Azm-MxvtE2(SyVskN>5o-KWp_HWl{aC%R9=V`dNz?l|}Wl=D$`J z)z5nCp|Yrc*0W2>qWW3Ke^M6J&${r2vZ#L6Yu_u2T46>1Hzv846IR^AGqWPp%Kc5k zDzU#3R@_%)QT?n$-AJ%lH^`#;S&99XU~ylOMfI~1d6-}^|Hz{HS&99XU~ylOMfI~1 m^)SI=9VCnDXC-P>g2mcI7S+#6?BxWDdzdV$vCtW{TJj&)wVrnX literal 0 HcmV?d00001 diff --git a/testdata/algo/deep_region_au19.gds b/testdata/algo/deep_region_au19.gds new file mode 100644 index 0000000000000000000000000000000000000000..0383ee5109763d71fee32ca096c5d5e0f4bef0c1 GIT binary patch literal 13978 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 X4;=OTdvJRp_m{Xa)o-Ke>T3Q6XFii( literal 0 HcmV?d00001