From 4f0b9118c362f99ff97938310e59ea59d70a3a07 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 13 Nov 2020 00:31:56 +0100 Subject: [PATCH] Added test for the collect modes, bugfixed the new modes. --- src/db/db/dbCommonReader.cc | 53 +++++----- .../streamers/gds2/unit_tests/dbGDS2Reader.cc | 97 ++++++++++++++++++ testdata/gds/collect_add_au.gds | Bin 0 -> 12990 bytes testdata/gds/collect_added.gds | Bin 0 -> 4990 bytes testdata/gds/collect_basic.gds | Bin 0 -> 9360 bytes testdata/gds/collect_overwrite_au.gds | Bin 0 -> 8008 bytes testdata/gds/collect_rename_au.gds | Bin 0 -> 13248 bytes testdata/gds/collect_skip_au.gds | Bin 0 -> 9598 bytes 8 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 testdata/gds/collect_add_au.gds create mode 100644 testdata/gds/collect_added.gds create mode 100644 testdata/gds/collect_basic.gds create mode 100644 testdata/gds/collect_overwrite_au.gds create mode 100644 testdata/gds/collect_rename_au.gds create mode 100644 testdata/gds/collect_skip_au.gds diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index 8728f986d..741cf1f0d 100644 --- a/src/db/db/dbCommonReader.cc +++ b/src/db/db/dbCommonReader.cc @@ -305,55 +305,60 @@ CommonReader::finish (db::Layout &layout) new_cells.insert (std::make_pair (i->second.second, i->first)); } - // NOTE: by iterating bottom up we don't need to update the layout (we need the parents for merge_cell) + std::vector > cells_with_conflict; + + // First treat all the cells without conflict for (db::Layout::bottom_up_iterator bu = layout.begin_bottom_up (); bu != layout.end_bottom_up (); ++bu) { db::cell_index_type ci_new = *bu; + std::map::const_iterator i = new_cells.find (ci_new); - if (new_cells.find (ci_new) == new_cells.end ()) { + if (i == new_cells.end ()) { // not a new cell continue; - } else if (! layout.is_valid_cell_index (ci_new)) { - // this can happen if the new cell has been deleted by "prune_subcells" - continue; } - std::map::const_iterator i = new_cells.find (ci_new); - std::pair c2n = layout.cell_by_name (i->second.c_str ()); db::cell_index_type ci_org = c2n.second; - if (c2n.first) { + // NOTE: proxy cells are never resolved. "RenameCell" is a plain and simple case. + if (c2n.first && m_cc_resolution != RenameCell && ! layout.cell (ci_org).is_proxy ()) { + cells_with_conflict.push_back (std::make_pair (ci_new, ci_org)); + } else { + layout.rename_cell (ci_new, layout.uniquify_cell_name (i->second.c_str ()).c_str ()); + } - if (m_cc_resolution == RenameCell || layout.cell (ci_org).is_proxy ()) { + } - // NOTE: we never reopen proxies (they are always local to their layout). Instead we - // always rename for proxies - layout.rename_cell (ci_new, layout.uniquify_cell_name (i->second.c_str ()).c_str ()); + // Then treat all the cells with conflict + for (std::vector >::const_iterator cc = cells_with_conflict.begin (); cc != cells_with_conflict.end (); ++cc) { - } else { + db::cell_index_type ci_new = cc->first; + db::cell_index_type ci_org = cc->second; - // we have a cell conflict + // we have a cell conflict - if (m_cc_resolution == OverwriteCell && ! layout.cell (ci_new).is_ghost_cell ()) { + layout.force_update (); - layout.prune_subcells (ci_org); - layout.cell (ci_org).clear_shapes (); + if (m_cc_resolution == OverwriteCell && ! layout.cell (ci_new).is_ghost_cell ()) { - } else if (m_cc_resolution == SkipNewCell && ! layout.cell (ci_org).is_ghost_cell ()) { + layout.prune_subcells (ci_org); + layout.cell (ci_org).clear_shapes (); - layout.prune_subcells (ci_new); - layout.cell (ci_new).clear_shapes (); + merge_cell (layout, ci_org, ci_new); - } + } else if (m_cc_resolution == SkipNewCell && ! layout.cell (ci_org).is_ghost_cell ()) { - merge_cell (layout, ci_org, ci_new); + layout.prune_subcells (ci_new); + layout.cell (ci_new).clear_shapes (); - } + // we need the instances of the cell we just cleaned + layout.force_update (); + merge_cell (layout, ci_org, ci_new); } else { - layout.rename_cell (ci_new, layout.uniquify_cell_name (i->second.c_str ()).c_str ()); + merge_cell (layout, ci_org, ci_new); } diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc index 58b4cb80f..838260bf3 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc @@ -436,3 +436,100 @@ TEST(3_AdvancedMapping) std::string fn_au (tl::testsrc () + "/testdata/gds/alm_au.gds"); db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); } + +TEST(4_CollectModeRename) +{ + db::Manager m (false); + db::Layout layout (&m); + + db::LoadLayoutOptions options; + options.get_options ().cell_conflict_resolution = db::CommonReader::RenameCell; + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + std::string fn_au (tl::testsrc () + "/testdata/gds/collect_rename_au.gds"); + db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); +} + +TEST(4_CollectModeOverwrite) +{ + db::Manager m (false); + db::Layout layout (&m); + + db::LoadLayoutOptions options; + options.get_options ().cell_conflict_resolution = db::CommonReader::OverwriteCell; + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + std::string fn_au (tl::testsrc () + "/testdata/gds/collect_overwrite_au.gds"); + db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); +} + +TEST(4_CollectModeSkip) +{ + db::Manager m (false); + db::Layout layout (&m); + + db::LoadLayoutOptions options; + options.get_options ().cell_conflict_resolution = db::CommonReader::SkipNewCell; + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + std::string fn_au (tl::testsrc () + "/testdata/gds/collect_skip_au.gds"); + db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); +} + +TEST(4_CollectModeAdd) +{ + db::Manager m (false); + db::Layout layout (&m); + + db::LoadLayoutOptions options; + options.get_options ().cell_conflict_resolution = db::CommonReader::AddToCell; + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds"); + db::Reader reader (file); + reader.read (layout, options); + } + + std::string fn_au (tl::testsrc () + "/testdata/gds/collect_add_au.gds"); + db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); +} + diff --git a/testdata/gds/collect_add_au.gds b/testdata/gds/collect_add_au.gds new file mode 100644 index 0000000000000000000000000000000000000000..ca0206610f735d387d7be8c495ec574f7d97e7bd GIT binary patch literal 12990 zcmeHNJ8xV?6rO$fdgE*?CkhWmE|6l8M2Uzu563|=%KCv6VI?W@aKghZeh^EJL9xX{ zqM)EbqM)LIf&z&qB|<4uA%yr5loV8mf(lXaeP`y}o!Q;HcO1ehL3gC}xqHvtbI&>7 zdCbn(B&J+mGUKK4@ULdCshelae)E)nZEEFY@#3s82P;D_H9tM~%1__^aen^($*;e> zb=8ayWk2_Hl1xlY%w4{8bz$l1^2JLpU&dEc9WkbUAmP1uoHKQkz zD{~8ri!+>KcyFKFJG%bP*3Pk+!;^BaZVqDJ(Y5vU<<+~mdwBAN$+N=Q+vaS(Z#fMk zoRKzX<+kNaTh1PjBxa1dy|vNW_T~@S`5RmB zt=-*TJ2tc4+T30fy{gu}J1r64kb4#LEM;uAK3s!&uL^zf)XB-$g=L$Rqf=6Dx(hhRE=pX~?APYGeQnMYT$d7^K$7~K7+RslFcnk!O zxkx-t7kCT=kNHSE&J=hI1dqJ#G3tJQG$zr|3ru!{$wU7pZ06*MuCbBX)Iz4YJzN{`KusUkymmmv?mZ*nuIX6d^3e9=P&k|D8pOc!_z1dpYh{Lr4> zm@HOVgXtl+Vi76Uy#wkYeY^Uw>3gPCPueF})f!G(_LWW9B%@rjedL{F$%$6^0m!N` zO3fGeuAjefjZf(fd$06)n{s~XPx~Axhs@&zpMy-1!|!&;IZlr5BTo0TMl~-v%{N7J zO4Z@6Rdc1O_TY;~r2RBkKIK}OyU}#2OVd@+^k-EQ)LpBFO2tpkSE3<(4Nb`pe2eEx zC!9PhYd0F6>(cOXj{=Qg?wo5JXaw6h@SNM}Eu0kFh<217rT0=wPZD!S<(d)9doGfl z_!uwOY{aH|YP|l~E7JmWd+CWx6FsT(5B9qJJSgtdE{O_9Wgh478R)I{C>48{xf;IE)U$x%viOYaqh>vT1Ktt0Y`lgFuPNP;Tc+&m+{O$ zu~4aRDWBh+rKh_r?U_%487%}xsm9K7!9?Sbm{2pd821Hh_Z&4B4}j8AscF>Nj@@8% zrc3k3Et`@gxz_KrKgL?fxE{&~PpBhh+z4far>Y{O&wq);ykpe$I=i3PS?9j$svnGY zMn7YU^}vrU#E-^Un#-&Uj47qerhc&n@tn^gRJ*uR%i`Y{>$HY*XYPr!tro7D1Pk#m zj3;G2mVd_w7*xKuw0Z~>yvlQy27L(_RDQEq2n80EPwYH>uDXctkW0$Jc_{@3l*igF z6qin)7%a3#w~^2;^`u7`a(&k3X|JRX02;5-s*7D7$JGbm3JRLKlYz=_brtD&9$ zC-Dg1lJl!WYZdoMwN`0{>_{C(eU$;K{g=7QA^C_^L`vD?k*DeU4c}Q$$(LTP6wk{V zjP)6HS%!U2WVAG!!G9`to`J+l36T!>?L7>TXBkL?lYw;YZ)1jjN&kZn*@i3;DYO=q z(vy$aPdUj2pMzLTIeXqrIoaoNF_vphJpaTXt{P8wY5bsbt)MGni7Tubw68ysnAP=! zi(6T5@Eu&AV(7bsA}{=3xw2a4z4nN~3f1*y7^UIfxfeyv%ve0|8p3EijUL1z=Jtwc z-GA&nG4bEh%!>FPZ)tYFC9)oI2eh*ueb$9FYX<8Q*KVvkp!^VPp{z$3uJd)(A>t-= zh&71MlM*9fs(1;uxThF{!x4j>*47604m)l6AFMv$T>pQQ!!ragVi3QmksrH=qw_Qt zb7BFpf6Kl@zaLpL6KOn=iJJY^u!d5?{)TuE&yJ2kN0%X? zd~%Wtz903?NB6|PjE8qC3>E{X+(CM3-pK)f88CW pCw5t^p2!^W=-BPHr70&v&+aOW97?5x-uF47_y6siVP~~!{sDSI4=4Zt literal 0 HcmV?d00001 diff --git a/testdata/gds/collect_added.gds b/testdata/gds/collect_added.gds new file mode 100644 index 0000000000000000000000000000000000000000..e4882b6d8354f758abd8171d00e5d3c2dcc31d6d GIT binary patch literal 4990 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 literal 0 HcmV?d00001