From db66a6ee74b74f0c40197fce1089f592146551ae Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Mar 2025 23:43:14 +0100 Subject: [PATCH 01/76] Base class of DEdgePairWithProperties was EdgePair, not DEdgePair --- src/db/db/gsiDeclDbEdgePair.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/gsiDeclDbEdgePair.cc b/src/db/db/gsiDeclDbEdgePair.cc index 8c34e5507..0ba631bde 100644 --- a/src/db/db/gsiDeclDbEdgePair.cc +++ b/src/db/db/gsiDeclDbEdgePair.cc @@ -370,7 +370,7 @@ static db::DEdgePairWithProperties *new_dedge_pair_with_properties2 (const db::D return new db::DEdgePairWithProperties (edge_pair, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); } -Class decl_DEdgePairWithProperties (decl_EdgePair, "db", "DEdgePairWithProperties", +Class decl_DEdgePairWithProperties (decl_DEdgePair, "db", "DEdgePairWithProperties", gsi::properties_support_methods () + constructor ("new", &new_dedge_pair_with_properties, gsi::arg ("edge_pair"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." From 227203cdd10eac485b959de13d17f084007e008a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Mar 2025 00:43:31 +0100 Subject: [PATCH 02/76] Providing a less strict overload resolution Downcast now has precedence over conversion constructors, hence less ambiguities Solution is implemented for - Ruby - Python - Expressions For Expressions: - The overload resolution is less evolved anyway - There was an additional bug preventing to pass arrays (hashes) in expressions --- src/gsi/gsi/gsiExpression.cc | 6 +- src/gsi/gsi/gsiVariantArgs.cc | 44 +++++++------- src/gsi/gsi/gsiVariantArgs.h | 3 +- src/gsi/unit_tests/gsiExpressionTests.cc | 36 ++++++++++++ src/pya/pya/pyaCallables.cc | 2 + src/pya/pya/pyaMarshal.cc | 2 +- src/rba/rba/rba.cc | 2 + src/rba/rba/rbaMarshal.cc | 2 +- src/rdb/rdb/gsiDeclRdb.cc | 5 ++ testdata/python/rdbTest.py | 72 +++++++++++++++++++++++ testdata/ruby/rdbTest.rb | 74 ++++++++++++++++++++++++ 11 files changed, 223 insertions(+), 25 deletions(-) diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index 4ac9fbc10..1727c2bc6 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -952,9 +952,11 @@ VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context const tl::Variant *arg = i >= int (args.size ()) ? get_kwarg (*a, kwargs) : &args[i]; if (! arg) { is_valid = a->spec ()->has_default (); - } else if (gsi::test_arg (*a, *arg, false /*strict*/)) { + } else if (gsi::test_arg (*a, *arg, false /*strict*/, false /*no object substitution*/)) { + sc += 100; + } else if (gsi::test_arg (*a, *arg, true /*loose*/, false /*no object substitution*/)) { ++sc; - } else if (test_arg (*a, *arg, true /*loose*/)) { + } else if (gsi::test_arg (*a, *arg, true /*loose*/, true /*with object substitution*/)) { // non-scoring match } else { is_valid = false; diff --git a/src/gsi/gsi/gsiVariantArgs.cc b/src/gsi/gsi/gsiVariantArgs.cc index 39dc0725e..5e1b4ffb7 100644 --- a/src/gsi/gsi/gsiVariantArgs.cc +++ b/src/gsi/gsi/gsiVariantArgs.cc @@ -46,12 +46,12 @@ inline void *get_object (tl::Variant &var) // ------------------------------------------------------------------- // Test if an argument can be converted to the given type -bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose); +bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose, bool object_substitution); template struct test_arg_func { - void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType & /*atype*/, bool /*loose*/) + void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType & /*atype*/, bool /*loose*/, bool /*object_substitution*/) { *ret = arg.can_convert_to (); } @@ -60,7 +60,16 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/) + void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/, bool /*object_substitution*/) + { + *ret = true; + } +}; + +template <> +struct test_arg_func +{ + void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/, bool /*object_substitution*/) { *ret = true; } @@ -69,7 +78,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose, bool object_substitution) { // allow nil of pointers if ((atype.is_ptr () || atype.is_cptr ()) && arg.is_nil ()) { @@ -77,7 +86,7 @@ struct test_arg_func return; } - if (arg.is_list ()) { + if (object_substitution && arg.is_list ()) { // we may implicitly convert an array into a constructor call of a target object - // for now we only check whether the number of arguments is compatible with the array given. @@ -104,9 +113,9 @@ struct test_arg_func const tl::VariantUserClassBase *cls = arg.user_cls (); if (! cls) { *ret = false; - } else if (! cls->gsi_cls ()->is_derived_from (atype.cls ()) && (! loose || ! cls->gsi_cls ()->can_convert_to(atype.cls ()))) { - *ret = false; - } else if ((atype.is_ref () || atype.is_ptr ()) && cls->is_const ()) { + } else if (! (cls->gsi_cls () == atype.cls () || + (loose && (cls->gsi_cls ()->is_derived_from (atype.cls ()) || + (object_substitution && cls->gsi_cls ()->can_convert_to (atype.cls ())))))) { *ret = false; } else { *ret = true; @@ -117,7 +126,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if (! arg.is_list ()) { *ret = false; @@ -129,7 +138,7 @@ struct test_arg_func *ret = true; for (tl::Variant::const_iterator v = arg.begin (); v != arg.end () && *ret; ++v) { - if (! test_arg (ainner, *v, loose)) { + if (! test_arg (ainner, *v, loose, true)) { *ret = false; } } @@ -139,7 +148,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { // Note: delegating that to the function avoids "injected class name used as template template expression" warning if (! arg.is_array ()) { @@ -152,16 +161,11 @@ struct test_arg_func const ArgType &ainner = *atype.inner (); const ArgType &ainner_k = *atype.inner_k (); - if (! arg.is_list ()) { - *ret = false; - return; - } - *ret = true; for (tl::Variant::const_array_iterator a = arg.begin_array (); a != arg.end_array () && *ret; ++a) { - if (! test_arg (ainner_k, a->first, loose)) { + if (! test_arg (ainner_k, a->first, loose, true)) { *ret = false; - } else if (! test_arg (ainner, a->second, loose)) { + } else if (! test_arg (ainner, a->second, loose, true)) { *ret = false; } } @@ -169,7 +173,7 @@ struct test_arg_func }; bool -test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose) +test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose, bool object_substitution) { // for const X * or X *, nil is an allowed value if ((atype.is_cptr () || atype.is_ptr ()) && arg.is_nil ()) { @@ -177,7 +181,7 @@ test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose) } bool ret = false; - gsi::do_on_type () (atype.type (), &ret, arg, atype, loose); + gsi::do_on_type () (atype.type (), &ret, arg, atype, loose, object_substitution); return ret; } diff --git a/src/gsi/gsi/gsiVariantArgs.h b/src/gsi/gsi/gsiVariantArgs.h index 75f312819..187132289 100644 --- a/src/gsi/gsi/gsiVariantArgs.h +++ b/src/gsi/gsi/gsiVariantArgs.h @@ -70,10 +70,11 @@ GSI_PUBLIC void pull_arg (gsi::SerialArgs &retlist, const gsi::ArgType &atype, t * @param atype The argument type * @param arg The value to pass to it * @param loose true for loose checking + * @param object_substitution true to substitute object arguments by lists (using constructor) or employing conversion constructors * * @return True, if the argument can be passed */ -GSI_PUBLIC bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose); +GSI_PUBLIC bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose, bool object_substitution); } diff --git a/src/gsi/unit_tests/gsiExpressionTests.cc b/src/gsi/unit_tests/gsiExpressionTests.cc index 0449850d5..105383e92 100644 --- a/src/gsi/unit_tests/gsiExpressionTests.cc +++ b/src/gsi/unit_tests/gsiExpressionTests.cc @@ -863,3 +863,39 @@ TEST(16) } } +// implicit conversions +TEST(17) +{ + tl::Eval e; + tl::Variant v; + + // smoke test + v = e.parse ("var rdb=ReportDatabase.new();" + "var cat=rdb.create_category('name');" + "var cell=rdb.create_cell('TOP');" + "var it=rdb.create_item(cell,cat);" + "var bwp=BoxWithProperties.new(Box.new(0,0,1,2), {1=>'value'});" + "it.add_value(bwp)").execute (); + + v = e.parse ("var rdb=ReportDatabase.new();" + "var cat=rdb.create_category('name');" + "var cell=rdb.create_cell('TOP');" + "var it=rdb.create_item(cell,cat);" + "var bwp=DBoxWithProperties.new(DBox.new(0,0,1,2), {1=>'value'});" + "it.add_value(bwp)").execute (); + + v = e.parse ("var rdb=ReportDatabase.new();" + "var cat=rdb.create_category('name');" + "var cell=rdb.create_cell('TOP');" + "var it=rdb.create_item(cell,cat);" + "var b=DBox.new(0,0,1,2);" + "it.add_value(b)").execute (); + + v = e.parse ("var rdb=ReportDatabase.new();" + "var cat=rdb.create_category('name');" + "var cell=rdb.create_cell('TOP');" + "var it=rdb.create_item(cell,cat);" + "it.add_value(17.5)").execute (); +} + + diff --git a/src/pya/pya/pyaCallables.cc b/src/pya/pya/pyaCallables.cc index f9fd2c75f..829f7076f 100644 --- a/src/pya/pya/pyaCallables.cc +++ b/src/pya/pya/pyaCallables.cc @@ -413,6 +413,8 @@ match_method (int mid, PyObject *self, PyObject *args, PyObject *kwargs, bool st if (! arg) { is_valid = a->spec ()->has_default (); } else if (test_arg (*a, arg.get (), false /*strict*/, false /*object substitution*/)) { + sc += 100; + } else if (test_arg (*a, arg.get (), true /*loose*/, false /*object substitution*/)) { ++sc; } else if (test_arg (*a, arg.get (), true /*loose*/, true /*object substitution*/)) { // non-scoring match diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index 7a50c85e7..0326f2e18 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -1232,7 +1232,7 @@ struct test_arg_func return; } - if (! (cls_decl == acls || (loose && (cls_decl->is_derived_from (atype.cls ()) || cls_decl->can_convert_to (atype.cls ()))))) { + if (! (cls_decl == acls || (loose && (cls_decl->is_derived_from (atype.cls ()) || (object_substitution && cls_decl->can_convert_to (atype.cls ())))))) { *ret = false; return; } diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 7979bc6cc..77faa3d88 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -539,6 +539,8 @@ private: if (arg == Qundef) { is_valid = a->spec ()->has_default (); } else if (test_arg (*a, arg, false /*strict*/, false /*with object substitution*/)) { + sc += 100; + } else if (test_arg (*a, arg, true /*loose*/, false /*with object substitution*/)) { ++sc; } else if (test_arg (*a, arg, true /*loose*/, true /*with object substitution*/)) { // non-scoring match diff --git a/src/rba/rba/rbaMarshal.cc b/src/rba/rba/rbaMarshal.cc index b55f82111..0781f5e7d 100644 --- a/src/rba/rba/rbaMarshal.cc +++ b/src/rba/rba/rbaMarshal.cc @@ -1217,7 +1217,7 @@ struct test_arg_func // in loose mode (second pass) try to match the types via implicit constructors, // in strict mode (first pass) require direct type match - if (p->cls_decl () == atype.cls () || (loose && (p->cls_decl ()->is_derived_from (atype.cls ()) || p->cls_decl ()->can_convert_to (atype.cls ())))) { + if (p->cls_decl () == atype.cls () || (loose && (p->cls_decl ()->is_derived_from (atype.cls ()) || (object_substitution && p->cls_decl ()->can_convert_to (atype.cls ()))))) { // type matches: check constness if ((atype.is_ref () || atype.is_ptr ()) && p->const_ref ()) { *ret = false; diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index 10307e96f..d2db8c0d1 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -999,6 +999,11 @@ Class decl_RdbItem ("rdb", "RdbItem", "@param value The box to add.\n" "This method has been introduced in version 0.25 as a convenience method." ) + + gsi::method_ext ("add_value", &add_value_t, gsi::arg ("value"), + "@brief Adds a text object to the values of this item\n" + "@param value The text to add.\n" + "This method has been introduced in version 0.30.1 to support text objects with properties." + ) + gsi::method_ext ("add_value", &add_value_t, gsi::arg ("value"), "@brief Adds an edge object to the values of this item\n" "@param value The edge to add.\n" diff --git a/testdata/python/rdbTest.py b/testdata/python/rdbTest.py index dfa2194d6..a7c34925f 100644 --- a/testdata/python/rdbTest.py +++ b/testdata/python/rdbTest.py @@ -972,6 +972,78 @@ class RDB_TestClass(unittest.TestCase): _cat_same = None self.assertEqual(_subcat.rdb_id(), _subcat_same.rdb_id()) + def test_15(self): + + p = pya.DPolygon(pya.DBox(0.5, 1, 2, 3)) + pwp = pya.DPolygonWithProperties(p, { 1: "value" }) + e = pya.DEdge(pya.DPoint(0, 0), pya.DPoint(1, 2)) + ewp = pya.DEdgeWithProperties(e, { 1: "value" }) + ep = pya.DEdgePair(e, e.moved(10, 10)) + epwp = pya.DEdgePairWithProperties(ep, { 1: "value" }) + t = pya.DText("text", pya.DTrans.R0) + twp = pya.DTextWithProperties(t, { 1: "value" }) + b = pya.DBox(0, 0, 1, 2) + bwp = pya.DBoxWithProperties(b, { 1: "value" }) + + ip = pya.Polygon(pya.Box(0, 1, 2, 3)) + ipwp = pya.PolygonWithProperties(ip, { 1: "value" }) + ie = pya.Edge(pya.Point(0, 0), pya.Point(1, 2)) + iewp = pya.EdgeWithProperties(ie, { 1: "value" }) + iep = pya.EdgePair(ie, ie.moved(10, 10)) + iepwp = pya.EdgePairWithProperties(iep, { 1: "value" }) + it = pya.Text("text", pya.Trans.R0) + itwp = pya.TextWithProperties(it, { 1: "value" }) + ib = pya.Box(0, 0, 1, 2) + ibwp = pya.BoxWithProperties(ib, { 1: "value" }) + + rdb = pya.ReportDatabase() + + cat = rdb.create_category("name") + cell = rdb.create_cell("TOP") + item = rdb.create_item(cell, cat) + + item.add_value(p) + item.add_value(pwp) + item.add_value(b) + item.add_value(bwp) + item.add_value(t) + item.add_value(twp) + item.add_value(e) + item.add_value(ewp) + item.add_value(ep) + item.add_value(epwp) + + item.add_value(ip) + item.add_value(ipwp) + item.add_value(ib) + item.add_value(ibwp) + item.add_value(it) + item.add_value(itwp) + item.add_value(ie) + item.add_value(iewp) + item.add_value(iep) + item.add_value(iepwp) + + item.add_value("string") + item.add_value(17.5) + + values = [ str(v) for v in item.each_value() ] + + self.assertEqual(values, [ + 'polygon: (0.5,1;0.5,3;2,3;2,1)', 'polygon: (0.5,1;0.5,3;2,3;2,1)', + 'box: (0,0;1,2)', 'box: (0,0;1,2)', + "label: ('text',r0 0,0)", "label: ('text',r0 0,0)", + 'edge: (0,0;1,2)', 'edge: (0,0;1,2)', + 'edge-pair: (0,0;1,2)/(10,10;11,12)', 'edge-pair: (0,0;1,2)/(10,10;11,12)', + 'polygon: (0,1;0,3;2,3;2,1)', 'polygon: (0,1;0,3;2,3;2,1)', + 'box: (0,0;1,2)', 'box: (0,0;1,2)', + "label: ('text',r0 0,0)", "label: ('text',r0 0,0)", + 'edge: (0,0;1,2)', 'edge: (0,0;1,2)', + 'edge-pair: (0,0;1,2)/(10,10;11,12)', 'edge-pair: (0,0;1,2)/(10,10;11,12)', + 'text: string', + 'float: 17.5' + ]) + # run unit tests if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(RDB_TestClass) diff --git a/testdata/ruby/rdbTest.rb b/testdata/ruby/rdbTest.rb index 392e0cfa4..91ecb9bf2 100644 --- a/testdata/ruby/rdbTest.rb +++ b/testdata/ruby/rdbTest.rb @@ -1131,6 +1131,80 @@ class RDB_TestClass < TestBase end + def test_15 + + p = RBA::DPolygon::new(RBA::DBox::new(0.5, 1, 2, 3)) + pwp = RBA::DPolygonWithProperties::new(p, { 1 => "value" }) + e = RBA::DEdge::new(RBA::DPoint::new(0, 0), RBA::DPoint::new(1, 2)) + ewp = RBA::DEdgeWithProperties::new(e, { 1 => "value" }) + ep = RBA::DEdgePair::new(e, e.moved(10, 10)) + epwp = RBA::DEdgePairWithProperties::new(ep, { 1 => "value" }) + t = RBA::DText::new("text", RBA::DTrans::R0) + twp = RBA::DTextWithProperties::new(t, { 1 => "value" }) + b = RBA::DBox::new(0, 0, 1, 2) + bwp = RBA::DBoxWithProperties::new(b, { 1 => "value" }) + + ip = RBA::Polygon::new(RBA::Box::new(0, 1, 2, 3)) + ipwp = RBA::PolygonWithProperties::new(ip, { 1 => "value" }) + ie = RBA::Edge::new(RBA::Point::new(0, 0), RBA::Point::new(1, 2)) + iewp = RBA::EdgeWithProperties::new(ie, { 1 => "value" }) + iep = RBA::EdgePair::new(ie, ie.moved(10, 10)) + iepwp = RBA::EdgePairWithProperties::new(iep, { 1 => "value" }) + it = RBA::Text::new("text", RBA::Trans::R0) + itwp = RBA::TextWithProperties::new(it, { 1 => "value" }) + ib = RBA::Box::new(0, 0, 1, 2) + ibwp = RBA::BoxWithProperties::new(ib, { 1 => "value" }) + + rdb = RBA::ReportDatabase::new + + cat = rdb.create_category("name") + cell = rdb.create_cell("TOP") + item = rdb.create_item(cell, cat) + + item.add_value(p) + item.add_value(pwp) + item.add_value(b) + item.add_value(bwp) + item.add_value(t) + item.add_value(twp) + item.add_value(e) + item.add_value(ewp) + item.add_value(ep) + item.add_value(epwp) + + item.add_value(ip) + item.add_value(ipwp) + item.add_value(ib) + item.add_value(ibwp) + item.add_value(it) + item.add_value(itwp) + item.add_value(ie) + item.add_value(iewp) + item.add_value(iep) + item.add_value(iepwp) + + item.add_value("string") + item.add_value(17.5) + + values = item.each_value.collect(&:to_s) + + assert_equal(values, [ + 'polygon: (0.5,1;0.5,3;2,3;2,1)', 'polygon: (0.5,1;0.5,3;2,3;2,1)', + 'box: (0,0;1,2)', 'box: (0,0;1,2)', + "label: ('text',r0 0,0)", "label: ('text',r0 0,0)", + 'edge: (0,0;1,2)', 'edge: (0,0;1,2)', + 'edge-pair: (0,0;1,2)/(10,10;11,12)', 'edge-pair: (0,0;1,2)/(10,10;11,12)', + 'polygon: (0,1;0,3;2,3;2,1)', 'polygon: (0,1;0,3;2,3;2,1)', + 'box: (0,0;1,2)', 'box: (0,0;1,2)', + "label: ('text',r0 0,0)", "label: ('text',r0 0,0)", + 'edge: (0,0;1,2)', 'edge: (0,0;1,2)', + 'edge-pair: (0,0;1,2)/(10,10;11,12)', 'edge-pair: (0,0;1,2)/(10,10;11,12)', + 'text: string', + 'float: 17.5' + ]) + + end + end load("test_epilogue.rb") From a24d5388d7db6101d2fcd5c8a8b55722b56c0317 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Mar 2025 00:56:54 +0100 Subject: [PATCH 03/76] Region[] now returns a PolygonWithProperties object or nil --- src/db/db/gsiDeclDbRegion.cc | 16 ++++++++++++++-- src/gsi/unit_tests/gsiExpressionTests.cc | 1 - testdata/ruby/dbRegionTest.rb | 6 +++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 11b80b936..53da87933 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -1379,6 +1379,16 @@ rasterize1 (const db::Region *region, const db::Point &origin, const db::Vector return rasterize2 (region, origin, pixel_size, pixel_size, nx, ny); } +static tl::Variant nth (const db::Region *region, size_t n) +{ + const db::Polygon *poly = region->nth (n); + if (! poly) { + return tl::Variant (); + } else { + return tl::Variant (db::PolygonWithProperties (*poly, region->nth_prop_id (n))); + } +} + static db::generic_shape_iterator begin_region (const db::Region *region) { return db::generic_shape_iterator (db::make_wp_iter (region->delegate ()->begin ())); @@ -4103,14 +4113,16 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "This returns the raw polygons if merged semantics is disabled or the merged ones if merged semantics is enabled.\n" "Starting with version 0.30, the iterator delivers a RegionWithProperties object." ) + - method ("[]", &db::Region::nth, gsi::arg ("n"), + method_ext ("[]", &nth, gsi::arg ("n"), "@brief Returns the nth polygon of the region\n" "\n" "This method returns nil if the index is out of range. It is available for flat regions only - i.e. " "those for which \\has_valid_polygons? is true. Use \\flatten to explicitly flatten a region.\n" "This method returns the raw polygon (not merged polygons, even if merged semantics is enabled).\n" "\n" - "The \\each iterator is the more general approach to access the polygons." + "The \\each iterator is the more general approach to access the polygons.\n" + "\n" + "Since version 0.30.1, this method returns a \\PolygonWithProperties object." ) + method ("flatten", &db::Region::flatten, "@brief Explicitly flattens a region\n" diff --git a/src/gsi/unit_tests/gsiExpressionTests.cc b/src/gsi/unit_tests/gsiExpressionTests.cc index 105383e92..a395a73d9 100644 --- a/src/gsi/unit_tests/gsiExpressionTests.cc +++ b/src/gsi/unit_tests/gsiExpressionTests.cc @@ -898,4 +898,3 @@ TEST(17) "it.add_value(17.5)").execute (); } - diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 5f8588f8b..e92798056 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -226,10 +226,14 @@ class DBRegion_TestClass < TestBase r.flatten assert_equal(r.has_valid_polygons?, true) - assert_equal(r[1].to_s, "(-10,80;-10,120;10,120;10,80)") + assert_equal(r[1].to_s, "(-10,80;-10,120;10,120;10,80) props={}") assert_equal(r[4].to_s, "") assert_equal(r.bbox.to_s, "(-10,-20;210,120)") assert_equal(r.is_merged?, false) + + r = RBA::Region::new + r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 10, 20), { 1 => 'value' })) + assert_equal(r[0].to_s, "(0,0;0,20;10,20;10,0) props={1=>value}") r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "*") assert_equal(csort(r.to_s), csort("(-11,-21;-11,-19;-9,-19;-9,-21);(9,19;9,21;11,21;11,19);(-11,79;-11,81;-9,81;-9,79);(9,119;9,121;11,121;11,119);(189,79;189,81;191,81;191,79);(209,119;209,121;211,121;211,119)")) From 3aebf90ecdb79e1feed1583ea0dff49f35e6cf14 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Mar 2025 01:22:54 +0100 Subject: [PATCH 04/76] Texts[n] now also provides a TextWithProperties object. Bugfix: this method was not delivering any objects with properties at all. --- src/db/db/dbDeepTexts.cc | 6 +++++ src/db/db/dbDeepTexts.h | 1 + src/db/db/dbEmptyTexts.h | 1 + src/db/db/dbFlatTexts.cc | 41 ++++++++++++++++++++++++++++++- src/db/db/dbFlatTexts.h | 1 + src/db/db/dbOriginalLayerTexts.cc | 6 +++++ src/db/db/dbOriginalLayerTexts.h | 1 + src/db/db/dbTexts.h | 13 +++++++++- src/db/db/dbTextsDelegate.h | 1 + src/db/db/gsiDeclDbEdgePairs.cc | 17 +++++++++++-- src/db/db/gsiDeclDbEdges.cc | 17 +++++++++++-- src/db/db/gsiDeclDbTexts.cc | 16 ++++++++++-- src/db/unit_tests/dbTextsTests.cc | 19 ++++++++++++++ testdata/ruby/dbRegionTest.rb | 4 ++- testdata/ruby/dbTextsTest.rb | 8 ++++-- 15 files changed, 141 insertions(+), 11 deletions(-) diff --git a/src/db/db/dbDeepTexts.cc b/src/db/db/dbDeepTexts.cc index e272e25ca..d15bb3601 100644 --- a/src/db/db/dbDeepTexts.cc +++ b/src/db/db/dbDeepTexts.cc @@ -332,6 +332,12 @@ const db::Text *DeepTexts::nth (size_t) const throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat text collections"))); } +db::properties_id_type +DeepTexts::nth_prop_id (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections"))); +} + bool DeepTexts::has_valid_texts () const { return false; diff --git a/src/db/db/dbDeepTexts.h b/src/db/db/dbDeepTexts.h index 7425df7cd..0c9a6eabb 100644 --- a/src/db/db/dbDeepTexts.h +++ b/src/db/db/dbDeepTexts.h @@ -71,6 +71,7 @@ public: virtual Box bbox () const; virtual bool empty () const; virtual const db::Text *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_texts () const; virtual const db::RecursiveShapeIterator *iter () const; virtual void apply_property_translator (const db::PropertiesTranslator &pt); diff --git a/src/db/db/dbEmptyTexts.h b/src/db/db/dbEmptyTexts.h index 37cdd6725..202a72399 100644 --- a/src/db/db/dbEmptyTexts.h +++ b/src/db/db/dbEmptyTexts.h @@ -71,6 +71,7 @@ public: virtual TextsDelegate *in (const Texts &, bool) const { return new EmptyTexts (); } virtual const db::Text *nth (size_t) const { tl_assert (false); } + virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); } virtual bool has_valid_texts () const { return true; } virtual const db::RecursiveShapeIterator *iter () const { return 0; } diff --git a/src/db/db/dbFlatTexts.cc b/src/db/db/dbFlatTexts.cc index e2e0e54a2..ef6dda20d 100644 --- a/src/db/db/dbFlatTexts.cc +++ b/src/db/db/dbFlatTexts.cc @@ -171,7 +171,46 @@ TextsDelegate *FlatTexts::add_in_place (const Texts &other) const db::Text *FlatTexts::nth (size_t n) const { - return n < mp_texts->size () ? &mp_texts->get_layer ().begin () [n] : 0; + // NOTE: this assumes that we iterate over non-property texts first and then over texts with properties + + if (n >= mp_texts->size ()) { + return 0; + } + + const db::layer &l = mp_texts->get_layer (); + if (n < l.size ()) { + return &l.begin () [n]; + } + n -= l.size (); + + const db::layer &lp = mp_texts->get_layer (); + if (n < lp.size ()) { + return &lp.begin () [n]; + } + + return 0; +} + +db::properties_id_type FlatTexts::nth_prop_id (size_t n) const +{ + // NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties + + if (n >= mp_texts->size ()) { + return 0; + } + + const db::layer &l = mp_texts->get_layer (); + if (n < l.size ()) { + return 0; + } + n -= l.size (); + + const db::layer &lp = mp_texts->get_layer (); + if (n < lp.size ()) { + return lp.begin () [n].properties_id (); + } + + return 0; } bool FlatTexts::has_valid_texts () const diff --git a/src/db/db/dbFlatTexts.h b/src/db/db/dbFlatTexts.h index f7e5ff16c..553dd58d5 100644 --- a/src/db/db/dbFlatTexts.h +++ b/src/db/db/dbFlatTexts.h @@ -76,6 +76,7 @@ public: virtual TextsDelegate *add (const Texts &other) const; virtual const db::Text *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_texts () const; virtual const db::RecursiveShapeIterator *iter () const; diff --git a/src/db/db/dbOriginalLayerTexts.cc b/src/db/db/dbOriginalLayerTexts.cc index e4f53eb27..af633ebe5 100644 --- a/src/db/db/dbOriginalLayerTexts.cc +++ b/src/db/db/dbOriginalLayerTexts.cc @@ -190,6 +190,12 @@ OriginalLayerTexts::nth (size_t) const throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections"))); } +db::properties_id_type +OriginalLayerTexts::nth_prop_id (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections"))); +} + bool OriginalLayerTexts::has_valid_texts () const { diff --git a/src/db/db/dbOriginalLayerTexts.h b/src/db/db/dbOriginalLayerTexts.h index d294e2803..81971bd44 100644 --- a/src/db/db/dbOriginalLayerTexts.h +++ b/src/db/db/dbOriginalLayerTexts.h @@ -53,6 +53,7 @@ public: virtual bool empty () const; virtual const db::Text *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_texts () const; virtual const db::RecursiveShapeIterator *iter () const; diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index a948f3366..51b05a5d6 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -495,7 +495,7 @@ public: /** * @brief Returns the nth text * - * This operation is available only for flat regions - i.e. such for which + * This operation is available only for flat text collections - i.e. such for which * "has_valid_texts" is true. */ const db::Text *nth (size_t n) const @@ -503,6 +503,17 @@ public: return mp_delegate->nth (n); } + /** + * @brief Returns the nth text's property ID + * + * This operation is available only for flat text collections - i.e. such for which + * "has_valid_texts" is true. + */ + db::properties_id_type nth_prop_id (size_t n) const + { + return mp_delegate->nth_prop_id (n); + } + /** * @brief Forces flattening of the text collection * diff --git a/src/db/db/dbTextsDelegate.h b/src/db/db/dbTextsDelegate.h index 4808e9679..895b1808e 100644 --- a/src/db/db/dbTextsDelegate.h +++ b/src/db/db/dbTextsDelegate.h @@ -109,6 +109,7 @@ public: virtual TextsDelegate *in (const Texts &other, bool invert) const = 0; virtual const db::Text *nth (size_t n) const = 0; + virtual db::properties_id_type nth_prop_id (size_t n) const = 0; virtual bool has_valid_texts () const = 0; virtual const db::RecursiveShapeIterator *iter () const = 0; diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 2a50e6747..282c917a8 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -783,6 +783,17 @@ static std::vector split_with_area2 (const db::EdgePairs *r, db:: return as_2edge_pairs_vector (r->split_filter (f)); } +static tl::Variant nth (const db::EdgePairs *edge_pairs, size_t n) +{ + const db::EdgePair *ep = edge_pairs->nth (n); + if (! ep) { + return tl::Variant (); + } else { + // @@@ return tl::Variant (db::EdgePairWithProperties (*ep, edge_pairs->nth_prop_id (n))); + return tl::Variant (); // @@@ + } +} + static db::generic_shape_iterator begin_edge_pairs (const db::EdgePairs *edge_pairs) { return db::generic_shape_iterator (db::make_wp_iter (edge_pairs->delegate ()->begin ())); @@ -1855,13 +1866,15 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "Starting with version 0.30, the iterator delivers EdgePairWithProperties objects." ) + - method ("[]", &db::EdgePairs::nth, gsi::arg ("n"), + method_ext ("[]", &nth, gsi::arg ("n"), "@brief Returns the nth edge pair\n" "\n" "This method returns nil if the index is out of range. It is available for flat edge pairs only - i.e. " "those for which \\has_valid_edge_pairs? is true. Use \\flatten to explicitly flatten an edge pair collection.\n" "\n" - "The \\each iterator is the more general approach to access the edge pairs." + "The \\each iterator is the more general approach to access the edge pairs.\n" + "\n" + "Since version 0.30.1, this method returns a \\EdgePairWithProperties object." ) + method ("flatten", &db::EdgePairs::flatten, "@brief Explicitly flattens an edge pair collection\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index d4da722f4..3eb98edc0 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -848,6 +848,17 @@ static std::vector split_interacting_with_region (const db::Edges *r, return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count)); } +static tl::Variant nth (const db::Edges *edges, size_t n) +{ + const db::Edge *e = edges->nth (n); + if (! e) { + return tl::Variant (); + } else { + // @@@ return tl::Variant (db::EdgeWithProperties (*e, edges->nth_prop_id (n))); + return tl::Variant (); // @@@ + } +} + static db::generic_shape_iterator begin_edges (const db::Edges *edges) { return db::generic_shape_iterator (db::make_wp_iter (edges->delegate ()->begin ())); @@ -2439,14 +2450,16 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "This method has been introduced in version 0.25." "Starting with version 0.30, the iterator delivers an EdgeWithProperties object." ) + - method ("[]", &db::Edges::nth, gsi::arg ("n"), + method_ext ("[]", &nth, gsi::arg ("n"), "@brief Returns the nth edge of the collection\n" "\n" "This method returns nil if the index is out of range. It is available for flat edge collections only - i.e. " "those for which \\has_valid_edges? is true. Use \\flatten to explicitly flatten an edge collection.\n" "This method returns the raw edge (not merged edges, even if merged semantics is enabled).\n" "\n" - "The \\each iterator is the more general approach to access the edges." + "The \\each iterator is the more general approach to access the edges.\n" + "\n" + "Since version 0.30.1, this method returns an \\EdgeWithProperties object." ) + method ("flatten", &db::Edges::flatten, "@brief Explicitly flattens an edge collection\n" diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 66e1d34a6..17a9563c9 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -455,6 +455,16 @@ static db::Region pull_interacting (const db::Texts *r, const db::Region &other) return out; } +static tl::Variant nth (const db::Texts *texts, size_t n) +{ + const db::Text *t = texts->nth (n); + if (! t) { + return tl::Variant (); + } else { + return tl::Variant (db::TextWithProperties (*t, texts->nth_prop_id (n))); + } +} + static db::generic_shape_iterator begin_texts (const db::Texts *texts) { return db::generic_shape_iterator (db::make_wp_iter (texts->delegate ()->begin ())); @@ -846,13 +856,15 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "\n" "Starting with version 0.30, the iterator delivers TextWithProperties objects." ) + - method ("[]", &db::Texts::nth, gsi::arg ("n"), + method_ext ("[]", &nth, gsi::arg ("n"), "@brief Returns the nth text\n" "\n" "This method returns nil if the index is out of range. It is available for flat texts only - i.e. " "those for which \\has_valid_texts? is true. Use \\flatten to explicitly flatten an text collection.\n" "\n" - "The \\each iterator is the more general approach to access the texts." + "The \\each iterator is the more general approach to access the texts.\n" + "\n" + "Since version 0.30.1, this method returns a \\TextWithProperties object." ) + method ("flatten", &db::Texts::flatten, "@brief Explicitly flattens an text collection\n" diff --git a/src/db/unit_tests/dbTextsTests.cc b/src/db/unit_tests/dbTextsTests.cc index 2fc3e652b..e7666647e 100644 --- a/src/db/unit_tests/dbTextsTests.cc +++ b/src/db/unit_tests/dbTextsTests.cc @@ -307,3 +307,22 @@ TEST(9_polygons) EXPECT_EQ (r.to_string (), "(9,19;9,21;11,21;11,19){17=>ABC};(-11,-21;-11,-19;-9,-19;-9,-21){17=>XZY}"); } +TEST(10_properties) +{ + db::PropertiesSet ps; + + ps.insert (tl::Variant ("id"), 1); + db::properties_id_type pid1 = db::properties_id (ps); + + db::Texts texts; + texts.insert (db::TextWithProperties (db::Text ("string", db::Trans ()), pid1)); + texts.insert (db::Text ("abc", db::Trans ())); + + EXPECT_EQ (texts.nth (0)->to_string (), "('abc',r0 0,0)"); + EXPECT_EQ (texts.nth (1)->to_string (), "('string',r0 0,0)"); + EXPECT_EQ (texts.nth (2) == 0, true); + + EXPECT_EQ (texts.nth_prop_id (0), db::properties_id_type (0)); + EXPECT_EQ (texts.nth_prop_id (1), pid1); +} + diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index e92798056..9ccdadf82 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -233,7 +233,9 @@ class DBRegion_TestClass < TestBase r = RBA::Region::new r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 10, 20), { 1 => 'value' })) - assert_equal(r[0].to_s, "(0,0;0,20;10,20;10,0) props={1=>value}") + r.insert(RBA::Box::new(1, 2, 11, 22)) + assert_equal(r[0].to_s, "@@@") + assert_equal(r[1].to_s, "(0,0;0,20;10,20;10,0) props={1=>value}") r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "*") assert_equal(csort(r.to_s), csort("(-11,-21;-11,-19;-9,-19;-9,-21);(9,19;9,21;11,21;11,19);(-11,79;-11,81;-9,81;-9,79);(9,119;9,121;11,121;11,119);(189,79;189,81;191,81;191,79);(209,119;209,121;211,121;211,119)")) diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index 145379068..4f481ef3f 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -103,7 +103,7 @@ class DBTexts_TestClass < TestBase assert_equal(r.is_empty?, false) assert_equal(r.count, 1) assert_equal(r.hier_count, 1) - assert_equal(r[0].to_s, "('uvw',r0 110,210)") + assert_equal(r[0].to_s, "('uvw',r0 110,210) props={}") assert_equal(r[1].to_s, "") assert_equal(r.bbox.to_s, "(110,210;110,210)") @@ -223,9 +223,13 @@ class DBTexts_TestClass < TestBase r.flatten assert_equal(r.has_valid_texts?, true) - assert_equal(r[1].to_s, "('abc',r0 100,-100)") + assert_equal(r[1].to_s, "('abc',r0 100,-100) props={}") assert_equal(r[100].inspect, "nil") assert_equal(r.bbox.to_s, "(100,-200;300,-100)") + + r = RBA::Texts::new + r.insert(RBA::TextWithProperties::new(RBA::Text::new("string", RBA::Trans::new), { 1 => "value" })) + assert_equal(r[0].to_s, "('string',r0 0,0) props={1=>value}") dss = RBA::DeepShapeStore::new r = RBA::Texts::new(ly.begin_shapes(c1.cell_index, l1), dss) From 40a0113ce529ebf5ed68e7a90f9613f35e1acec2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Mar 2025 00:27:53 +0100 Subject: [PATCH 05/76] Fixing Texts[], EdgePairs[] and Edges[] operators - both in terms of returning xWithProperties and objects with properties at all --- src/db/db/dbDeepEdgePairs.cc | 5 ++++ src/db/db/dbDeepEdgePairs.h | 1 + src/db/db/dbDeepEdges.cc | 6 ++++ src/db/db/dbDeepEdges.h | 1 + src/db/db/dbEdgePairs.h | 13 ++++++++- src/db/db/dbEdgePairsDelegate.h | 1 + src/db/db/dbEdges.h | 13 ++++++++- src/db/db/dbEdgesDelegate.h | 1 + src/db/db/dbEmptyEdgePairs.h | 1 + src/db/db/dbEmptyEdges.h | 1 + src/db/db/dbFlatEdgePairs.cc | 41 ++++++++++++++++++++++++++- src/db/db/dbFlatEdgePairs.h | 1 + src/db/db/dbFlatEdges.cc | 41 ++++++++++++++++++++++++++- src/db/db/dbFlatEdges.h | 1 + src/db/db/dbOriginalLayerEdgePairs.cc | 6 ++++ src/db/db/dbOriginalLayerEdgePairs.h | 1 + src/db/db/dbOriginalLayerEdges.cc | 6 ++++ src/db/db/dbOriginalLayerEdges.h | 1 + src/db/db/gsiDeclDbEdgePairs.cc | 3 +- src/db/db/gsiDeclDbEdges.cc | 3 +- src/db/unit_tests/dbEdgePairsTests.cc | 21 ++++++++++++++ src/db/unit_tests/dbEdgesTests.cc | 19 +++++++++++++ testdata/ruby/dbEdgePairsTest.rb | 5 ++-- testdata/ruby/dbEdgesTest.rb | 7 +++-- 24 files changed, 186 insertions(+), 13 deletions(-) diff --git a/src/db/db/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc index 7593067c9..41938c618 100644 --- a/src/db/db/dbDeepEdgePairs.cc +++ b/src/db/db/dbDeepEdgePairs.cc @@ -311,6 +311,11 @@ const db::EdgePair *DeepEdgePairs::nth (size_t) const throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat edge pair collections"))); } +db::properties_id_type DeepEdgePairs::nth_prop_id (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat edge pair collections"))); +} + bool DeepEdgePairs::has_valid_edge_pairs () const { return false; diff --git a/src/db/db/dbDeepEdgePairs.h b/src/db/db/dbDeepEdgePairs.h index c55e7f97f..7ed036d6e 100644 --- a/src/db/db/dbDeepEdgePairs.h +++ b/src/db/db/dbDeepEdgePairs.h @@ -70,6 +70,7 @@ public: virtual Box bbox () const; virtual bool empty () const; virtual const db::EdgePair *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edge_pairs () const; virtual const db::RecursiveShapeIterator *iter () const; virtual void apply_property_translator (const db::PropertiesTranslator &pt); diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index bd80f7ac9..3872386fe 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -438,6 +438,12 @@ DeepEdges::nth (size_t /*n*/) const throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat edge collections"))); } +db::properties_id_type +DeepEdges::nth_prop_id (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections"))); +} + bool DeepEdges::has_valid_edges () const { diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h index 6af2bf55a..b8cf9fc2e 100644 --- a/src/db/db/dbDeepEdges.h +++ b/src/db/db/dbDeepEdges.h @@ -76,6 +76,7 @@ public: virtual bool is_merged () const; virtual const db::Edge *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edges () const; virtual bool has_valid_merged_edges () const; diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 271777473..0cffe8960 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -678,7 +678,7 @@ public: /** * @brief Returns the nth edge pair * - * This operation is available only for flat regions - i.e. such for which + * This operation is available only for flat edge pair collections - i.e. such for which * "has_valid_edge_pairs" is true. */ const db::EdgePair *nth (size_t n) const @@ -686,6 +686,17 @@ public: return mp_delegate->nth (n); } + /** + * @brief Returns the nth edge pair's property ID + * + * This operation is available only for flat edge pair collections - i.e. such for which + * "has_valid_edge_pairs" is true. + */ + db::properties_id_type nth_prop_id (size_t n) const + { + return mp_delegate->nth_prop_id (n); + } + /** * @brief Forces flattening of the edge pair collection * diff --git a/src/db/db/dbEdgePairsDelegate.h b/src/db/db/dbEdgePairsDelegate.h index d8dd18849..d4a1efd97 100644 --- a/src/db/db/dbEdgePairsDelegate.h +++ b/src/db/db/dbEdgePairsDelegate.h @@ -273,6 +273,7 @@ public: virtual EdgePairsDelegate *in (const EdgePairs &other, bool invert) const = 0; virtual const db::EdgePair *nth (size_t n) const = 0; + virtual db::properties_id_type nth_prop_id (size_t n) const = 0; virtual bool has_valid_edge_pairs () const = 0; virtual const db::RecursiveShapeIterator *iter () const = 0; diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 6b507048a..31bdc35af 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -1440,13 +1440,24 @@ public: /** * @brief Returns the nth edge * - * This operation is available only for flat regions - i.e. such for which "has_valid_edges" is true. + * This operation is available only for flat edge collections - i.e. such for which "has_valid_edges" is true. */ const db::Edge *nth (size_t n) const { return mp_delegate->nth (n); } + /** + * @brief Returns the nth edge's property ID + * + * This operation is available only for flat edge collections - i.e. such for which + * "has_valid_edges" is true. + */ + db::properties_id_type nth_prop_id (size_t n) const + { + return mp_delegate->nth_prop_id (n); + } + /** * @brief Forces flattening of the edge collection * diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 496b86931..91aea92ee 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -280,6 +280,7 @@ public: virtual std::pair in_and_out (const Edges &) const = 0; virtual const db::Edge *nth (size_t n) const = 0; + virtual db::properties_id_type nth_prop_id (size_t n) const = 0; virtual bool has_valid_edges () const = 0; virtual bool has_valid_merged_edges () const = 0; diff --git a/src/db/db/dbEmptyEdgePairs.h b/src/db/db/dbEmptyEdgePairs.h index f63cfd1a8..92650b923 100644 --- a/src/db/db/dbEmptyEdgePairs.h +++ b/src/db/db/dbEmptyEdgePairs.h @@ -89,6 +89,7 @@ public: virtual EdgePairsDelegate *in (const EdgePairs &, bool) const { return new EmptyEdgePairs (); } virtual const db::EdgePair *nth (size_t) const { tl_assert (false); } + virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); } virtual bool has_valid_edge_pairs () const { return true; } virtual const db::RecursiveShapeIterator *iter () const { return 0; } diff --git a/src/db/db/dbEmptyEdges.h b/src/db/db/dbEmptyEdges.h index 84032b448..bf45753cd 100644 --- a/src/db/db/dbEmptyEdges.h +++ b/src/db/db/dbEmptyEdges.h @@ -119,6 +119,7 @@ public: virtual std::pair in_and_out (const Edges &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); } virtual const db::Edge *nth (size_t) const { tl_assert (false); } + virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); } virtual bool has_valid_edges () const { return true; } virtual bool has_valid_merged_edges () const { return true; } diff --git a/src/db/db/dbFlatEdgePairs.cc b/src/db/db/dbFlatEdgePairs.cc index cb554a3ea..a42bce475 100644 --- a/src/db/db/dbFlatEdgePairs.cc +++ b/src/db/db/dbFlatEdgePairs.cc @@ -173,7 +173,46 @@ EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other) const db::EdgePair *FlatEdgePairs::nth (size_t n) const { - return n < mp_edge_pairs->size () ? &mp_edge_pairs->get_layer ().begin () [n] : 0; + // NOTE: this assumes that we iterate over non-property edge pairs first and then over edges with properties + + if (n >= mp_edge_pairs->size ()) { + return 0; + } + + const db::layer &l = mp_edge_pairs->get_layer (); + if (n < l.size ()) { + return &l.begin () [n]; + } + n -= l.size (); + + const db::layer &lp = mp_edge_pairs->get_layer (); + if (n < lp.size ()) { + return &lp.begin () [n]; + } + + return 0; +} + +db::properties_id_type FlatEdgePairs::nth_prop_id (size_t n) const +{ + // NOTE: this assumes that we iterate over non-property edge pairs first and then over edges with properties + + if (n >= mp_edge_pairs->size ()) { + return 0; + } + + const db::layer &l = mp_edge_pairs->get_layer (); + if (n < l.size ()) { + return 0; + } + n -= l.size (); + + const db::layer &lp = mp_edge_pairs->get_layer (); + if (n < lp.size ()) { + return lp.begin () [n].properties_id (); + } + + return 0; } bool FlatEdgePairs::has_valid_edge_pairs () const diff --git a/src/db/db/dbFlatEdgePairs.h b/src/db/db/dbFlatEdgePairs.h index dc38dc088..ce203c4aa 100644 --- a/src/db/db/dbFlatEdgePairs.h +++ b/src/db/db/dbFlatEdgePairs.h @@ -75,6 +75,7 @@ public: virtual EdgePairsDelegate *add (const EdgePairs &other) const; virtual const db::EdgePair *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edge_pairs () const; virtual const db::RecursiveShapeIterator *iter () const; diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index 44299bc8c..27e323cae 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -361,7 +361,46 @@ EdgesDelegate *FlatEdges::add_in_place (const Edges &other) const db::Edge *FlatEdges::nth (size_t n) const { - return n < mp_edges->size () ? &mp_edges->get_layer ().begin () [n] : 0; + // NOTE: this assumes that we iterate over non-property edges first and then over edges with properties + + if (n >= mp_edges->size ()) { + return 0; + } + + const db::layer &l = mp_edges->get_layer (); + if (n < l.size ()) { + return &l.begin () [n]; + } + n -= l.size (); + + const db::layer &lp = mp_edges->get_layer (); + if (n < lp.size ()) { + return &lp.begin () [n]; + } + + return 0; +} + +db::properties_id_type FlatEdges::nth_prop_id (size_t n) const +{ + // NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties + + if (n >= mp_edges->size ()) { + return 0; + } + + const db::layer &l = mp_edges->get_layer (); + if (n < l.size ()) { + return 0; + } + n -= l.size (); + + const db::layer &lp = mp_edges->get_layer (); + if (n < lp.size ()) { + return lp.begin () [n].properties_id (); + } + + return 0; } bool FlatEdges::has_valid_edges () const diff --git a/src/db/db/dbFlatEdges.h b/src/db/db/dbFlatEdges.h index f5464be30..551c33ce4 100644 --- a/src/db/db/dbFlatEdges.h +++ b/src/db/db/dbFlatEdges.h @@ -89,6 +89,7 @@ public: virtual EdgesDelegate *add (const Edges &other) const; virtual const db::Edge *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edges () const; virtual bool has_valid_merged_edges () const; diff --git a/src/db/db/dbOriginalLayerEdgePairs.cc b/src/db/db/dbOriginalLayerEdgePairs.cc index 301208ebc..5038a1885 100644 --- a/src/db/db/dbOriginalLayerEdgePairs.cc +++ b/src/db/db/dbOriginalLayerEdgePairs.cc @@ -190,6 +190,12 @@ OriginalLayerEdgePairs::nth (size_t) const throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat collections"))); } +db::properties_id_type +OriginalLayerEdgePairs::nth_prop_id (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat collections"))); +} + bool OriginalLayerEdgePairs::has_valid_edge_pairs () const { diff --git a/src/db/db/dbOriginalLayerEdgePairs.h b/src/db/db/dbOriginalLayerEdgePairs.h index bcecf1f7c..41000da51 100644 --- a/src/db/db/dbOriginalLayerEdgePairs.h +++ b/src/db/db/dbOriginalLayerEdgePairs.h @@ -53,6 +53,7 @@ public: virtual bool empty () const; virtual const db::EdgePair *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edge_pairs () const; virtual const db::RecursiveShapeIterator *iter () const; diff --git a/src/db/db/dbOriginalLayerEdges.cc b/src/db/db/dbOriginalLayerEdges.cc index a01314308..3484b926e 100644 --- a/src/db/db/dbOriginalLayerEdges.cc +++ b/src/db/db/dbOriginalLayerEdges.cc @@ -233,6 +233,12 @@ OriginalLayerEdges::nth (size_t) const throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections"))); } +db::properties_id_type +OriginalLayerEdges::nth_prop_id (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections"))); +} + bool OriginalLayerEdges::has_valid_edges () const { diff --git a/src/db/db/dbOriginalLayerEdges.h b/src/db/db/dbOriginalLayerEdges.h index 89d6fc647..53254e852 100644 --- a/src/db/db/dbOriginalLayerEdges.h +++ b/src/db/db/dbOriginalLayerEdges.h @@ -58,6 +58,7 @@ public: virtual bool is_merged () const; virtual const db::Edge *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edges () const; virtual bool has_valid_merged_edges () const; diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 282c917a8..903aba802 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -789,8 +789,7 @@ static tl::Variant nth (const db::EdgePairs *edge_pairs, size_t n) if (! ep) { return tl::Variant (); } else { - // @@@ return tl::Variant (db::EdgePairWithProperties (*ep, edge_pairs->nth_prop_id (n))); - return tl::Variant (); // @@@ + return tl::Variant (db::EdgePairWithProperties (*ep, edge_pairs->nth_prop_id (n))); } } diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 3eb98edc0..0383d14ac 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -854,8 +854,7 @@ static tl::Variant nth (const db::Edges *edges, size_t n) if (! e) { return tl::Variant (); } else { - // @@@ return tl::Variant (db::EdgeWithProperties (*e, edges->nth_prop_id (n))); - return tl::Variant (); // @@@ + return tl::Variant (db::EdgeWithProperties (*e, edges->nth_prop_id (n))); } } diff --git a/src/db/unit_tests/dbEdgePairsTests.cc b/src/db/unit_tests/dbEdgePairsTests.cc index 1f6153c3d..1a8680fdd 100644 --- a/src/db/unit_tests/dbEdgePairsTests.cc +++ b/src/db/unit_tests/dbEdgePairsTests.cc @@ -279,3 +279,24 @@ TEST(6_add_with_properties) EXPECT_EQ ((ro1 + rf2).to_string (), "(10,20;-20,60)/(10,30;-20,70){net=>17};(-10,20;20,60)/(-10,30;20,70){net=>17}"); } +TEST(7_properties) +{ + db::PropertiesSet ps; + + ps.insert (tl::Variant ("id"), 1); + db::properties_id_type pid1 = db::properties_id (ps); + + db::EdgePairs edge_pairs; + db::Edge e1 (db::Point (0, 0), db::Point (10, 20)); + db::Edge e2 (db::Point (1, 2), db::Point (11, 22)); + edge_pairs.insert (db::EdgePairWithProperties (db::EdgePair (e1, e2), pid1)); + edge_pairs.insert (db::EdgePair (e1, e2)); + + EXPECT_EQ (edge_pairs.nth (0)->to_string (), "(0,0;10,20)/(1,2;11,22)"); + EXPECT_EQ (edge_pairs.nth (1)->to_string (), "(0,0;10,20)/(1,2;11,22)"); + EXPECT_EQ (edge_pairs.nth (2) == 0, true); + + EXPECT_EQ (edge_pairs.nth_prop_id (0), db::properties_id_type (0)); + EXPECT_EQ (edge_pairs.nth_prop_id (1), pid1); +} + diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index 15a4b562b..0db2b9676 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -1443,6 +1443,25 @@ TEST(32_add_with_properties) EXPECT_EQ ((ro1 + rf2).to_string (), "(10,20;40,60){net=>17};(-10,20;20,60){net=>17}"); } +TEST(33_properties) +{ + db::PropertiesSet ps; + + ps.insert (tl::Variant ("id"), 1); + db::properties_id_type pid1 = db::properties_id (ps); + + db::Edges edges; + edges.insert (db::EdgeWithProperties (db::Edge (db::Point (0, 0), db::Point (10, 20)), pid1)); + edges.insert (db::Edge (db::Point (0, 0), db::Point (10, 20))); + + EXPECT_EQ (edges.nth (0)->to_string (), "(0,0;10,20)"); + EXPECT_EQ (edges.nth (1)->to_string (), "(0,0;10,20)"); + EXPECT_EQ (edges.nth (2) == 0, true); + + EXPECT_EQ (edges.nth_prop_id (0), db::properties_id_type (0)); + EXPECT_EQ (edges.nth_prop_id (1), pid1); +} + // GitHub issue #72 (Edges/Region NOT issue) TEST(100) { diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index d67bc33c6..ac157ff53 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -111,7 +111,7 @@ class DBEdgePairs_TestClass < TestBase assert_equal(csort(r.edges.to_s), csort("(0,0;0,100);(-10,0;-20,50)")) assert_equal(r.is_empty?, false) assert_equal(r.size, 1) - assert_equal(r[0].to_s, "(0,0;0,100)/(-10,0;-20,50)") + assert_equal(r[0].to_s, "(0,0;0,100)/(-10,0;-20,50) props={}") assert_equal(r[1].to_s, "") assert_equal(r.bbox.to_s, "(-20,0;0,100)") @@ -221,7 +221,7 @@ class DBEdgePairs_TestClass < TestBase r.flatten assert_equal(r.has_valid_edge_pairs?, true) - assert_equal(r[1].to_s, "(0,101;2,103)/(10,111;12,113)") + assert_equal(r[1].to_s, "(0,101;2,103)/(10,111;12,113) props={}") assert_equal(r[100].inspect, "nil") assert_equal(r.bbox.to_s, "(0,1;212,113)") @@ -622,6 +622,7 @@ class DBEdgePairs_TestClass < TestBase r = RBA::EdgePairs::new([ RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;100,100)/(200,300;200,500){1=>one}") + assert_equal(r[0].to_s, "(0,0;100,100)/(200,300;200,500) props={1=>one}") r = RBA::EdgePairs::new([]) assert_equal(r.to_s, "") diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 086a0eb9c..7a8047b23 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -235,7 +235,7 @@ class DBEdges_TestClass < TestBase assert_equal(r.is_empty?, false) assert_equal(r.count, 12) assert_equal(r.hier_count, 12) - assert_equal(r[1].to_s, "(-10,20;10,20)") + assert_equal(r[1].to_s, "(-10,20;10,20) props={}") assert_equal(r[100].to_s, "") assert_equal(r.bbox.to_s, "(-10,-20;210,120)") assert_equal(r.is_merged?, false) @@ -252,11 +252,11 @@ class DBEdges_TestClass < TestBase r.flatten assert_equal(r.has_valid_edges?, true) - assert_equal(r[1].to_s, "(-10,80;10,120)") + assert_equal(r[1].to_s, "(-10,80;10,120) props={}") assert_equal(r[100].to_s, "") assert_equal(r.bbox.to_s, "(-10,-20;210,120)") assert_equal(r.is_merged?, false) - + r = RBA::Edges::new(ly.begin_shapes(c1.cell_index, l1), RBA::ICplxTrans::new(10, 20), true) assert_equal(r.to_s(30), "(0,0;0,40);(0,40;20,40);(20,40;20,0);(20,0;0,0);(0,100;0,140);(0,140;20,140);(20,140;20,100);(20,100;0,100);(200,100;200,140);(200,140;220,140);(220,140;220,100);(220,100;200,100)") assert_equal(r.is_empty?, false) @@ -970,6 +970,7 @@ class DBEdges_TestClass < TestBase r = RBA::Edges::new([ RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;100,100){1=>one}") + assert_equal(r[0].to_s, "(0,0;100,100) props={1=>one}") r = RBA::Edges::new([]) assert_equal(r.to_s, "") From a38bea30860d2ea096799a0e1df7fe634e68d253 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Mar 2025 19:35:26 +0100 Subject: [PATCH 06/76] Fixed a unit test --- testdata/ruby/dbRegionTest.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 9ccdadf82..985dd9406 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -234,7 +234,7 @@ class DBRegion_TestClass < TestBase r = RBA::Region::new r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 10, 20), { 1 => 'value' })) r.insert(RBA::Box::new(1, 2, 11, 22)) - assert_equal(r[0].to_s, "@@@") + assert_equal(r[0].to_s, "(1,2;1,22;11,22;11,2) props={}") assert_equal(r[1].to_s, "(0,0;0,20;10,20;10,0) props={1=>value}") r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "*") From 0a587fa079b171c6a71f63402ac0897defc2e823 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 21:10:10 +0000 Subject: [PATCH 07/76] Bump pypa/cibuildwheel from 2.23.0 to 2.23.2 Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.23.0 to 2.23.2. - [Release notes](https://github.com/pypa/cibuildwheel/releases) - [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md) - [Commits](https://github.com/pypa/cibuildwheel/compare/v2.23.0...v2.23.2) --- updated-dependencies: - dependency-name: pypa/cibuildwheel dependency-version: 2.23.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f87194b2c..02b0beaba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,7 +60,7 @@ jobs: HOST_CCACHE_DIR="$(ccache -k cache_dir)" mkdir -p $HOST_CCACHE_DIR - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions - uses: pypa/cibuildwheel@v2.23.0 + uses: pypa/cibuildwheel@v2.23.2 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value From c656700b44deb03a80106997ae5d28dcaaaba203 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 3 Apr 2025 20:08:33 +0200 Subject: [PATCH 08/76] Maybe fixed issue-2012 (leaking reference in Python) --- src/pya/pya/pyaConvert.cc | 3 +- src/pya/pya/pyaObject.cc | 60 +++++++++++++++++++++++---------- testdata/python/dbLayoutTest.py | 2 +- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/pya/pya/pyaConvert.cc b/src/pya/pya/pyaConvert.cc index c7b7c6f64..89ecf851f 100644 --- a/src/pya/pya/pyaConvert.cc +++ b/src/pya/pya/pyaConvert.cc @@ -440,7 +440,7 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo obj = clsact->create_from_adapted (obj); } - // we wil own the new object + // we will own the new object pass_obj = true; } @@ -488,6 +488,7 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo PYAObjectBase *new_object = PYAObjectBase::from_pyobject_unsafe (new_pyobject); new (new_object) PYAObjectBase (clsact, new_pyobject); new_object->set (obj, pass_obj, is_const, can_destroy); + return new_pyobject; } diff --git a/src/pya/pya/pyaObject.cc b/src/pya/pya/pyaObject.cc index 63f1e8517..1805b68b8 100644 --- a/src/pya/pya/pyaObject.cc +++ b/src/pya/pya/pyaObject.cc @@ -238,9 +238,16 @@ PYAObjectBase::object_destroyed () detach (); - // NOTE: this may delete "this"! - if (!prev_owner) { - Py_DECREF (py_object ()); + if (! prev_owner) { + const gsi::ClassBase *cls = cls_decl (); + if (cls && cls->is_managed ()) { + // If the object was owned on C++ side before, we need to decrement the + // reference count to reflect the fact, that there no longer is an external + // owner. + // NOTE: this may delete "this", hence we return + Py_DECREF (py_object ()); + return; + } } } @@ -249,31 +256,44 @@ PYAObjectBase::object_destroyed () void PYAObjectBase::release () { + // "release" means to release ownership of the C++ object on the C++ side. + // In other words: to transfer ownership to the script side. Specifically to + // transfer it to the Python domain. + // If the object is managed we first reset the ownership of all other clients // and then make us the owner const gsi::ClassBase *cls = cls_decl (); if (cls && cls->is_managed ()) { void *o = obj (); if (o) { + // NOTE: "keep" means "move ownership of the C++ object to C++". In other words, + // release ownership of the C++ object on script side. cls->gsi_object (o)->keep (); + if (! m_owned) { + // We have to *decrement* the reference count as now there is no other entity + // holding a reference to this Python object. + // NOTE: this may delete "this", hence we return + m_owned = true; + Py_DECREF (py_object ()); + return; + } } } - // NOTE: this is fairly dangerous - if (!m_owned) { - m_owned = true; - // NOTE: this may delete "this"! TODO: this should not happen. Can we assert that somehow? - Py_DECREF (py_object ()); - } + m_owned = true; } void PYAObjectBase::keep_internal () { if (m_owned) { + // "keep" means to transfer ownership of the C++ object to C++ side, while + // "m_owned" refers to ownership on the Python side. So if we perform this + // transfer, we need to reflect the fact that there is another entity holding + // a reference. Py_INCREF (py_object ()); - m_owned = false; } + m_owned = false; } void @@ -284,9 +304,11 @@ PYAObjectBase::keep () void *o = obj (); if (o) { if (cls->is_managed ()) { + // dispatch the keep notification - this will call "keep_internal" through the + // event handler (StatusChangedListener) cls->gsi_object (o)->keep (); } else { - keep_internal (); + m_owned = false; } } } @@ -341,16 +363,18 @@ PYAObjectBase::set (void *obj, bool owned, bool const_ref, bool can_destroy) if (cls->is_managed ()) { gsi::ObjectBase *gsi_object = cls->gsi_object (m_obj); - // Consider the case of "keep inside constructor" if (gsi_object->already_kept ()) { - keep_internal (); + // Consider the case of "keep inside constructor" + m_owned = false; + } + if (! m_owned) { + // "m_owned = false" means ownership of the C++ object is on C++ side, + // and not on script side. In that case, we need to increment the + // reference count to reflect the fact that there is an external owner. + Py_INCREF (py_object ()); } gsi_object->status_changed_event ().add (mp_listener, &StatusChangedListener::object_status_changed); } - - if (!m_owned) { - Py_INCREF (py_object ()); - } } // TODO: a static (singleton) instance is not thread-safe @@ -587,7 +611,7 @@ PYAObjectBase::obj () throw tl::Exception (tl::to_string (tr ("Object has been destroyed already"))); } else { // delayed creation of a detached C++ object .. - set(cls_decl ()->create (), true, false, true); + set (cls_decl ()->create (), true, false, true); } } diff --git a/testdata/python/dbLayoutTest.py b/testdata/python/dbLayoutTest.py index 1179f898b..bae65e913 100644 --- a/testdata/python/dbLayoutTest.py +++ b/testdata/python/dbLayoutTest.py @@ -684,7 +684,7 @@ class DBLayoutTest(unittest.TestCase): ly = pya.Layout(True) pv = [ [ 17, "a" ], [ "b", [ 1, 5, 7 ] ] ] pid = ly.properties_id( pv ) - # does not work? @@@ + # does not work? # pv = { 17: "a", "b": [ 1, 5, 7 ] } # pid2 = ly.properties_id( pv ) # self.assertEqual( pid, pid2 ) From 78e2074b4c236a93a8900f3ed700afb4af39529d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 3 Apr 2025 21:15:49 +0200 Subject: [PATCH 09/76] Added a unit test --- testdata/python/dbShapesTest.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/testdata/python/dbShapesTest.py b/testdata/python/dbShapesTest.py index d7e65f11a..09fa12fce 100644 --- a/testdata/python/dbShapesTest.py +++ b/testdata/python/dbShapesTest.py @@ -24,7 +24,7 @@ import os class DBShapesTest(unittest.TestCase): # Shape objects as hashes - def test_12(self): + def test_1(self): s = pya.Shapes() s1 = s.insert(pya.Box(1, 2, 3, 4)) @@ -50,6 +50,18 @@ class DBShapesTest(unittest.TestCase): self.assertEqual(h[s2], 2) self.assertEqual(h[s3], 3) + # Issue #2012 (reference count) + def test_2(self): + + ly = pya.Layout() + top = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + top.shapes(l1).insert(pya.Box(0, 0, 100, 200)) + + shapes = top.shapes(l1) + self.assertEqual(sys.getrefcount(shapes), 2) + + # run unit tests if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(DBShapesTest) From aa124487470be45c4b680c2a4a25cfe18327e083 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Apr 2025 20:35:11 +0200 Subject: [PATCH 10/76] First attempt to implement a solution for issue #2016 The implementation will not update the PCell on property sheet edits of the guiding shape if lazy evaluation is requested. Still, changes are committed to the PCell on committing the property page. --- src/ant/ant/antPropertiesPage.cc | 2 +- src/ant/ant/antPropertiesPage.h | 2 +- src/edt/edt/edtInstPropertiesPage.cc | 4 ++-- src/edt/edt/edtInstPropertiesPage.h | 4 ++-- src/edt/edt/edtPropertiesPages.cc | 12 +++++------ src/edt/edt/edtPropertiesPages.h | 6 +++--- src/edt/edt/edtService.cc | 29 ++++++++++++++++++++------ src/edt/edt/edtService.h | 9 ++++++-- src/img/img/imgPropertiesPage.cc | 6 +++--- src/img/img/imgPropertiesPage.h | 2 +- src/img/img/imgService.cc | 2 +- src/laybasic/laybasic/layProperties.h | 9 ++++++-- src/layui/layui/layPropertiesDialog.cc | 16 +++++++------- src/layui/layui/layPropertiesDialog.h | 2 +- 14 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/ant/ant/antPropertiesPage.cc b/src/ant/ant/antPropertiesPage.cc index 6b081bf21..15e19315e 100644 --- a/src/ant/ant/antPropertiesPage.cc +++ b/src/ant/ant/antPropertiesPage.cc @@ -517,7 +517,7 @@ PropertiesPage::readonly () } void -PropertiesPage::apply () +PropertiesPage::apply (bool /*commit*/) { ant::Object obj; get_object (obj); diff --git a/src/ant/ant/antPropertiesPage.h b/src/ant/ant/antPropertiesPage.h index 9b9438d71..c98c42d91 100644 --- a/src/ant/ant/antPropertiesPage.h +++ b/src/ant/ant/antPropertiesPage.h @@ -50,7 +50,7 @@ public: virtual void update (); virtual void leave (); virtual bool readonly (); - virtual void apply (); + virtual void apply (bool commit); private slots: void swap_points_clicked (); diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index da06cb814..de27a4d90 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -925,7 +925,7 @@ InstPropertiesPage::do_apply (bool current_only, bool relative) } void -InstPropertiesPage::apply () +InstPropertiesPage::apply (bool /*commit*/) { do_apply (true, false); } @@ -937,7 +937,7 @@ InstPropertiesPage::can_apply_to_all () const } void -InstPropertiesPage::apply_to_all (bool relative) +InstPropertiesPage::apply_to_all (bool relative, bool /*commit*/) { do_apply (false, relative); } diff --git a/src/edt/edt/edtInstPropertiesPage.h b/src/edt/edt/edtInstPropertiesPage.h index c33d7f3d6..f00a1a659 100644 --- a/src/edt/edt/edtInstPropertiesPage.h +++ b/src/edt/edt/edtInstPropertiesPage.h @@ -66,8 +66,8 @@ protected: edt::PCellParametersPage *mp_pcell_parameters; virtual bool readonly (); - virtual void apply (); - virtual void apply_to_all (bool relative); + virtual void apply (bool commit); + virtual void apply_to_all (bool relative, bool commit); virtual bool can_apply_to_all () const; void do_apply (bool current_only, bool relative); virtual ChangeApplicator *create_applicator (db::Cell &cell, const db::Instance &inst, double dbu); diff --git a/src/edt/edt/edtPropertiesPages.cc b/src/edt/edt/edtPropertiesPages.cc index 365ca1c63..26b1def30 100644 --- a/src/edt/edt/edtPropertiesPages.cc +++ b/src/edt/edt/edtPropertiesPages.cc @@ -215,7 +215,7 @@ ShapePropertiesPage::recompute_selection_ptrs (const std::vector gs = mp_service->handle_guiding_shape_changes (new_sel[index]); + std::pair gs = mp_service->handle_guiding_shape_changes (new_sel[index], commit); if (gs.first) { new_sel[index] = gs.second; @@ -350,9 +350,9 @@ ShapePropertiesPage::do_apply (bool current_only, bool relative) } void -ShapePropertiesPage::apply () +ShapePropertiesPage::apply (bool commit) { - do_apply (true, false); + do_apply (true, false, commit); } bool @@ -362,9 +362,9 @@ ShapePropertiesPage::can_apply_to_all () const } void -ShapePropertiesPage::apply_to_all (bool relative) +ShapePropertiesPage::apply_to_all (bool relative, bool commit) { - do_apply (false, relative); + do_apply (false, relative, commit); } void diff --git a/src/edt/edt/edtPropertiesPages.h b/src/edt/edt/edtPropertiesPages.h index 7d8d5b644..60d844f50 100644 --- a/src/edt/edt/edtPropertiesPages.h +++ b/src/edt/edt/edtPropertiesPages.h @@ -63,10 +63,10 @@ protected: private: virtual void update (); - virtual void apply (); - virtual void apply_to_all (bool relative); + virtual void apply (bool commit); + virtual void apply_to_all (bool relative, bool commit); virtual bool can_apply_to_all () const; - virtual void do_apply (bool current_only, bool relative); + virtual void do_apply (bool current_only, bool relative, bool commit); void recompute_selection_ptrs (const std::vector &new_sel); protected: diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 75fbf5428..aeb4223d1 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -76,6 +76,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIter m_snap_to_objects (true), m_snap_objects_to_grid (true), m_top_level_sel (false), m_show_shapes_of_instances (true), m_max_shapes_of_instances (1000), + m_pcell_lazy_evaluation (0), m_hier_copy_mode (-1), m_indicate_secondary_selection (false), m_seq (0), @@ -391,6 +392,10 @@ Service::configure (const std::string &name, const std::string &value) tl::from_string (value, m_hier_copy_mode); service_configuration_changed (); + } else if (name == cfg_edit_pcell_lazy_eval_mode) { + + tl::from_string (value, m_pcell_lazy_evaluation); + } else { lay::EditorServiceBase::configure (name, value); } @@ -598,7 +603,7 @@ Service::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type ac) transform (db::DCplxTrans (m_move_trans)); move_cancel (); // formally this functionality fits here // accept changes to guiding shapes - handle_guiding_shape_changes (); + handle_guiding_shape_changes (true); } m_alt_ac = lay::AC_Global; } @@ -846,7 +851,7 @@ Service::transform (const db::DCplxTrans &trans, const std::vector -Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const +Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj, bool commit) const { unsigned int cv_index = obj.cv_index (); lay::CellView cv = view ()->cellview (cv_index); @@ -1874,10 +1879,22 @@ Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const return std::make_pair (false, lay::ObjectInstPath ()); } - if (! layout->is_pcell_instance (obj.cell_index ()).first) { + auto pcell_decl = layout->pcell_declaration_for_pcell_variant (obj.cell_index ()); + if (! pcell_decl) { return std::make_pair (false, lay::ObjectInstPath ()); } + // Don't update unless we're committing or not in lazy PCell update mode + if (! commit) { + if (m_pcell_lazy_evaluation < 0) { + if (pcell_decl->wants_lazy_evaluation ()) { + return std::make_pair (false, lay::ObjectInstPath ()); + } + } else if (m_pcell_lazy_evaluation > 0) { + return std::make_pair (false, lay::ObjectInstPath ()); + } + } + db::cell_index_type top_cell = std::numeric_limits::max (); db::cell_index_type parent_cell = std::numeric_limits::max (); db::Instance parent_inst; @@ -1944,7 +1961,7 @@ Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const } bool -Service::handle_guiding_shape_changes () +Service::handle_guiding_shape_changes (bool commit) { EditableSelectionIterator s = begin_selection (); @@ -1953,7 +1970,7 @@ Service::handle_guiding_shape_changes () return false; } - std::pair gs = handle_guiding_shape_changes (*s); + std::pair gs = handle_guiding_shape_changes (*s, commit); if (gs.first) { // remove superfluous proxies diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 7e2d34801..e782bbfb5 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -398,17 +398,19 @@ public: * * @return true, if PCells have been updated, indicating that our selection is no longer valid * + * @param commit If true, changes are "final" (and PCells are updated also in lazy evaluation mode) + * * This version assumes there is only one guiding shape selected and will update the selection. * It will also call layout.cleanup() if required. */ - bool handle_guiding_shape_changes (); + bool handle_guiding_shape_changes (bool commit); /** * @brief Handle changes in a specific guiding shape, i.e. create new PCell variants if required * * @return A pair of bool (indicating that the object path has changed) and the new guiding shape path */ - std::pair handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const; + std::pair handle_guiding_shape_changes (const lay::ObjectInstPath &obj, bool commit) const; /** * @brief Gets a value indicating whether a move operation is ongoing @@ -672,9 +674,12 @@ private: bool m_snap_to_objects; bool m_snap_objects_to_grid; db::DVector m_global_grid; + + // Other attributes bool m_top_level_sel; bool m_show_shapes_of_instances; unsigned int m_max_shapes_of_instances; + int m_pcell_lazy_evaluation; // Hierarchical copy mode (-1: ask, 0: shallow, 1: deep) int m_hier_copy_mode; diff --git a/src/img/img/imgPropertiesPage.cc b/src/img/img/imgPropertiesPage.cc index 4ff7a954b..056e7e5ce 100644 --- a/src/img/img/imgPropertiesPage.cc +++ b/src/img/img/imgPropertiesPage.cc @@ -784,7 +784,7 @@ PropertiesPage::reverse_color_order () } void -PropertiesPage::apply () +PropertiesPage::apply (bool /*commit*/) { bool has_error = false; @@ -915,7 +915,7 @@ PropertiesPage::browse () { BEGIN_PROTECTED - apply (); + apply (true); lay::FileDialog file_dialog (this, tl::to_string (QObject::tr ("Load Image File")), tl::to_string (QObject::tr ("All files (*)"))); @@ -941,7 +941,7 @@ PropertiesPage::save_pressed () { BEGIN_PROTECTED - apply (); + apply (true); lay::FileDialog file_dialog (this, tl::to_string (QObject::tr ("Save As KLayout Image File")), tl::to_string (QObject::tr ("KLayout image files (*.lyimg);;All files (*)"))); diff --git a/src/img/img/imgPropertiesPage.h b/src/img/img/imgPropertiesPage.h index b9ccedc33..e715f99cd 100644 --- a/src/img/img/imgPropertiesPage.h +++ b/src/img/img/imgPropertiesPage.h @@ -57,7 +57,7 @@ public: virtual void update (); virtual void leave (); virtual bool readonly (); - virtual void apply (); + virtual void apply (bool commit); void set_direct_image (img::Object *image); diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index 0dc06ae74..3b1c85589 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -71,7 +71,7 @@ public: BEGIN_PROTECTED properties_frame->set_direct_image (mp_image_object); - properties_frame->apply (); + properties_frame->apply (true); if (mp_image_object->is_empty ()) { throw tl::Exception (tl::to_string (tr ("No data loaded for that image"))); diff --git a/src/laybasic/laybasic/layProperties.h b/src/laybasic/laybasic/layProperties.h index ef4b8ec3c..09b16c2b9 100644 --- a/src/laybasic/laybasic/layProperties.h +++ b/src/laybasic/laybasic/layProperties.h @@ -154,8 +154,10 @@ public: * Apply any changes to the current objects. If nothing was * changed, the object may be left untouched. * The dialog will start a transaction on the manager object. + * + * @param commit Is true for the "final" changes (i.e. not during editing) */ - virtual void apply () + virtual void apply (bool /*commit*/) { // default implementation is empty. } @@ -174,8 +176,11 @@ public: * Apply any changes to the current object plus all other objects of the same kind. * If nothing was changed, the objects may be left untouched. * The dialog will start a transaction on the manager object. + * + * @param relative Is true if relative mode is selected + * @param commit Is true for the "final" changes (i.e. not during editing) */ - virtual void apply_to_all (bool /*relative*/) + virtual void apply_to_all (bool /*relative*/, bool /*commit*/) { // default implementation is empty. } diff --git a/src/layui/layui/layPropertiesDialog.cc b/src/layui/layui/layPropertiesDialog.cc index b714742e8..edd85844b 100644 --- a/src/layui/layui/layPropertiesDialog.cc +++ b/src/layui/layui/layPropertiesDialog.cc @@ -197,7 +197,7 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, } for (size_t i = 0; i < mp_properties_pages.size (); ++i) { mp_stack->addWidget (mp_properties_pages [i]); - connect (mp_properties_pages [i], SIGNAL (edited ()), this, SLOT (apply ())); + connect (mp_properties_pages [i], SIGNAL (edited ()), this, SLOT (properties_edited ())); } // Necessary to maintain the page order for UI regression testing of 0.18 vs. 0.19 (because tl::Collection has changed to order) .. @@ -314,7 +314,7 @@ PropertiesDialog::current_index_changed (const QModelIndex &index, const QModelI db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); - mp_properties_pages [m_index]->apply (); + mp_properties_pages [m_index]->apply (true); if (! t.is_empty ()) { m_transaction_id = t.id (); @@ -437,7 +437,7 @@ BEGIN_PROTECTED if (! mp_properties_pages [m_index]->readonly ()) { db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); - mp_properties_pages [m_index]->apply (); + mp_properties_pages [m_index]->apply (true); if (! t.is_empty ()) { m_transaction_id = t.id (); } @@ -485,7 +485,7 @@ BEGIN_PROTECTED if (! mp_properties_pages [m_index]->readonly ()) { db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); - mp_properties_pages [m_index]->apply (); + mp_properties_pages [m_index]->apply (true); if (! t.is_empty ()) { m_transaction_id = t.id (); } @@ -567,7 +567,7 @@ PropertiesDialog::any_prev () const } void -PropertiesDialog::apply () +PropertiesDialog::properties_edited () { BEGIN_PROTECTED @@ -580,9 +580,9 @@ BEGIN_PROTECTED try { if (mp_ui->apply_to_all_cbx->isChecked () && mp_properties_pages [m_index]->can_apply_to_all ()) { - mp_properties_pages [m_index]->apply_to_all (mp_ui->relative_cbx->isChecked ()); + mp_properties_pages [m_index]->apply_to_all (mp_ui->relative_cbx->isChecked (), false); } else { - mp_properties_pages [m_index]->apply (); + mp_properties_pages [m_index]->apply (false); } mp_properties_pages [m_index]->update (); @@ -632,7 +632,7 @@ BEGIN_PROTECTED db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); - mp_properties_pages [m_index]->apply (); + mp_properties_pages [m_index]->apply (true); mp_properties_pages [m_index]->update (); if (! t.is_empty ()) { diff --git a/src/layui/layui/layPropertiesDialog.h b/src/layui/layui/layPropertiesDialog.h index 153002127..01e09db45 100644 --- a/src/layui/layui/layPropertiesDialog.h +++ b/src/layui/layui/layPropertiesDialog.h @@ -105,7 +105,7 @@ private: void update_controls (); public slots: - void apply (); + void properties_edited (); void next_pressed (); void prev_pressed (); void cancel_pressed (); From bcf14ede3e1e839bc8f057570ba54165f3e5b9f8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Apr 2025 22:06:29 +0200 Subject: [PATCH 11/76] Fixed issue #2014 (strm2oas lef/def/gds collect drops cells referenced by sky130 spare) Problem was that there was the implicit assumption that substitution cells would be top cells (or at least: not child cells of other substitution cells). --- src/db/db/dbCellMapping.cc | 48 ++++++++++++++----------- src/db/unit_tests/dbCellMappingTests.cc | 35 +++++++++++++++++- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index b3bdd4e6a..7bc3dc712 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -354,6 +354,13 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & std::vector &new_cells = *(new_cells_ptr ? new_cells_ptr : &new_cells_int); std::vector new_cells_b; + std::vector > all_a2b; + for (std::vector::const_iterator b = cell_index_b.begin (); b != cell_index_b.end (); ++b) { + auto m = m_b2a_mapping.find (*b); + tl_assert (m != m_b2a_mapping.end ()); + all_a2b.push_back (std::make_pair (m->second, *b)); + } + std::set called_b; for (std::vector::const_iterator i = cell_index_b.begin (); i != cell_index_b.end (); ++i) { layout_b.cell (*i).collect_called_cells (called_b); @@ -368,6 +375,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b); new_cells.push_back (new_cell); new_cells_b.push_back (*b); + all_a2b.push_back (std::make_pair (new_cell, *b)); if (mapped_pairs) { mapped_pairs->push_back (std::make_pair (*b, new_cell)); @@ -378,34 +386,34 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & } } - if (! new_cells.empty ()) { + if (all_a2b.empty ()) { + return; + } - // Note: this avoids frequent cell index table rebuilds if source and target layout are identical - db::LayoutLocker locker (&layout_a); + // Note: this avoids frequent cell index table rebuilds if source and target layout are identical + db::LayoutLocker locker (&layout_a); - // Create instances for the new cells in layout A according to their instantiation in layout B - double mag = layout_b.dbu () / layout_a.dbu (); - for (size_t i = 0; i < new_cells.size (); ++i) { + // Create instances for the new cells in layout A according to their instantiation in layout B + double mag = layout_b.dbu () / layout_a.dbu (); + for (auto i = all_a2b.begin (); i != all_a2b.end (); ++i) { - const db::Cell &b = layout_b.cell (new_cells_b [i]); - for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) { + const db::Cell &b = layout_b.cell (i->second); + for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) { - if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) { + if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) { - db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]); + db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]); - db::Instance bi = pb->child_inst (); + db::Instance bi = pb->child_inst (); - db::CellInstArray bci = bi.cell_inst (); - bci.object ().cell_index (new_cells [i]); - bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ()); - - if (bi.has_prop_id ()) { - pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ())); - } else { - pa.insert (bci); - } + db::CellInstArray bci = bi.cell_inst (); + bci.object ().cell_index (i->first); + bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ()); + if (bi.has_prop_id ()) { + pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ())); + } else { + pa.insert (bci); } } diff --git a/src/db/unit_tests/dbCellMappingTests.cc b/src/db/unit_tests/dbCellMappingTests.cc index b20c8ec66..f6ec6a767 100644 --- a/src/db/unit_tests/dbCellMappingTests.cc +++ b/src/db/unit_tests/dbCellMappingTests.cc @@ -485,8 +485,41 @@ TEST(7) cib.push_back (b1.cell_index ()); cib.push_back (b2.cell_index ()); cm.create_multi_mapping_full (h, cib, *g, cia); - EXPECT_EQ (m2s (cm, *g, h), "a0->b0;a1->b1;a2->b2;a3->a3;a4->a4;a5->a5"); + EXPECT_EQ (m2s (cm, h, *g), "b0->a0;b1->a1;b2->a2;a3->a3;a4->a4;a5->a5"); EXPECT_EQ (l2s (h), "b0#0:;b1#1:cell_index=3 r0 0,0,cell_index=4 r0 0,0;b2#2:cell_index=4 r0 0,0;a3#3:cell_index=4 r0 0,0,cell_index=5 r0 0,0;a4#4:;a5#5:"); } +// Issue #2014 +TEST(8) +{ + std::unique_ptr g (new db::Layout ()); + db::Cell &a (g->cell (g->add_cell ("a"))); + db::Cell &b (g->cell (g->add_cell ("b"))); + db::Cell &b1 (g->cell (g->add_cell ("b1"))); + db::Cell &b2 (g->cell (g->add_cell ("b2"))); + db::Cell &c (g->cell (g->add_cell ("c"))); + + b.insert (db::CellInstArray (db::CellInst (a.cell_index ()), db::Trans ())); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans ())); + b.insert (db::CellInstArray (db::CellInst (b1.cell_index ()), db::Trans ())); + b.insert (db::CellInstArray (db::CellInst (b2.cell_index ()), db::Trans ())); + + db::Layout h; + db::Cell &ha (h.cell (h.add_cell ("a"))); + db::Cell &hb (h.cell (h.add_cell ("b"))); + db::Cell &hc (h.cell (h.add_cell ("c"))); + + db::CellMapping cm; + std::vector cib, cia; + cia.push_back (a.cell_index ()); + cia.push_back (b.cell_index ()); + cia.push_back (c.cell_index ()); + cib.push_back (ha.cell_index ()); + cib.push_back (hb.cell_index ()); + cib.push_back (hc.cell_index ()); + cm.create_multi_mapping_full (h, cib, *g, cia); + EXPECT_EQ (m2s (cm, h, *g), "a->a;b->b;b1->b1;b2->b2;c->c"); + + EXPECT_EQ (l2s (h), "a#0:;b#1:cell_index=0 r0 0,0,cell_index=2 r0 0,0,cell_index=3 r0 0,0,cell_index=4 r0 0,0;c#2:;b1#3:;b2#4:"); +} From 163c3b8edc1ffeb73cd10ef8464ee084b0de9957 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Apr 2025 12:50:34 +0200 Subject: [PATCH 12/76] Making "assume FOREIGN always default for strm* tools", OASIS warns on ghost cells --- src/buddies/src/bd/bdReaderOptions.cc | 4 +++- src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index 53133d373..752d20edd 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -120,7 +120,9 @@ GenericReaderOptions::GenericReaderOptions () m_lefdef_separate_groups = load_options.get_option_by_name ("lefdef_config.separate_groups").to_bool (); m_lefdef_joined_paths = load_options.get_option_by_name ("lefdef_config.joined_paths").to_bool (); m_lefdef_map_file = load_options.get_option_by_name ("lefdef_config.map_file").to_string (); - m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int (); + // Don't take the default, as in practice, it's more common to substitute LEF macros by layouts + // m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int (); + m_lefdef_macro_resolution_mode = 2; // "assume FOREIGN always" } void diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 548a4e447..6a7f5afee 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1671,6 +1671,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save // skip cell body if the cell is not to be written if (skip_cell_body (cref)) { + tl::warn << tl::to_string (tr ("Cannot write ghost cell to OASIS - skipping cell: ")) << layout.cell_name (*cell); continue; } From 83e0c172910d19862028eb5da49a3dd6e26aa1a8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Apr 2025 19:20:38 +0200 Subject: [PATCH 13/76] Print total runtime for converter buddy tools with -d 11 --- src/buddies/src/bd/bdConverterMain.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/buddies/src/bd/bdConverterMain.cc b/src/buddies/src/bd/bdConverterMain.cc index d44f9b05e..d3f418b06 100644 --- a/src/buddies/src/bd/bdConverterMain.cc +++ b/src/buddies/src/bd/bdConverterMain.cc @@ -27,6 +27,7 @@ #include "dbReader.h" #include "dbWriter.h" #include "tlCommandLineParser.h" +#include "tlTimer.h" namespace bd { @@ -53,6 +54,8 @@ int converter_main (int argc, char *argv[], const std::string &format) db::Layout layout; + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + { db::LoadLayoutOptions load_options; generic_reader_options.configure (load_options); From 6b5268e5f768c35d35a3d6ca33db90fd32345552 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Apr 2025 19:21:02 +0200 Subject: [PATCH 14/76] Feature glob expansion on LEF and GDS lists for LEF/DEF reader options. --- .../lefdef/db_plugin/dbLEFDEFImporter.cc | 25 +++-- .../lefdef/db_plugin/dbLEFDEFImporter.h | 2 +- .../lefdef/db_plugin/dbLEFDEFPlugin.cc | 56 +++++++----- src/tl/tl/tlFileUtils.cc | 48 ++++++++++ src/tl/tl/tlFileUtils.h | 8 ++ src/tl/unit_tests/tlFileUtilsTests.cc | 91 +++++++++++++++++++ 6 files changed, 198 insertions(+), 32 deletions(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index ff229aeb9..d95144c75 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -38,7 +38,7 @@ namespace db // ----------------------------------------------------------------------------------- // Path resolution utility -std::string correct_path (const std::string &fn_in, const db::Layout &layout, const std::string &base_path) +std::vector correct_path (const std::string &fn_in, const db::Layout &layout, const std::string &base_path, bool glob) { const db::Technology *tech = layout.technology (); @@ -64,19 +64,28 @@ std::string correct_path (const std::string &fn_in, const db::Layout &layout, co if (tech && ! tech->base_path ().empty ()) { std::string new_fn = tl::combine_path (tech->base_path (), fn); if (tl::file_exists (new_fn)) { - return new_fn; + std::vector res; + res.push_back (new_fn); + return res; + } else if (glob) { + return tl::glob_expand (new_fn); } } if (! base_path.empty ()) { - return tl::combine_path (base_path, fn); - } else { - return fn; + fn = tl::combine_path (base_path, fn); } - } else { - return fn; } + + if (tl::file_exists (fn) || ! glob) { + std::vector res; + res.push_back (fn); + return res; + } else { + return tl::glob_expand (fn); + } + } // ----------------------------------------------------------------------------------- @@ -1059,7 +1068,7 @@ LEFDEFReaderState::read_map_file (const std::string &filename, db::Layout &layou std::map, std::vector > layer_map; for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - read_single_map_file (correct_path (*p, layout, base_path), layer_map); + read_single_map_file (correct_path (*p, layout, base_path, false).front (), layer_map); } // build an explicit layer mapping now. diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index 607410b6a..2a5736f0e 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -52,7 +52,7 @@ struct MacroDesc; * @brief Correct a path relative to the stream and technology */ DB_PLUGIN_PUBLIC -std::string correct_path (const std::string &fn, const db::Layout &layout, const std::string &base_path); +std::vector correct_path (const std::string &fn, const db::Layout &layout, const std::string &base_path, bool glob); /** * @brief Convers a string to a MASKSHIFT index list diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 8d37fa283..c8d3d840c 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -141,11 +141,12 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti for (std::vector::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { - std::string lp = correct_path (*l, layout, base_path); - - tl::InputStream lef_stream (lp); - tl::log << tl::to_string (tr ("Reading")) << " " << lp; - importer.read (lef_stream, layout, state); + auto paths = correct_path (*l, layout, base_path, true); + for (auto lp = paths.begin (); lp != paths.end (); ++lp) { + tl::InputStream lef_stream (*lp); + tl::log << tl::to_string (tr ("Reading")) << " " << *lp; + importer.read (lef_stream, layout, state); + } } @@ -164,14 +165,20 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti for (std::vector::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { - std::string lp = correct_path (*l, layout, base_path); - lef_files_read.insert (tl::normalize_path (lp)); + auto paths = correct_path (*l, layout, base_path, true); + for (auto lp = paths.begin (); lp != paths.end (); ++lp) { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + lp); + if (lef_files_read.insert (tl::normalize_path (*lp)).second) { - tl::InputStream lef_stream (lp); - tl::log << tl::to_string (tr ("Reading")) << " " << lp; - importer.read_lef (lef_stream, layout, state); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + *lp); + + tl::InputStream lef_stream (*lp); + tl::log << tl::to_string (tr ("Reading")) << " " << *lp; + importer.read_lef (lef_stream, layout, state); + + } + + } } @@ -223,22 +230,25 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti tl::shared_collection macro_layout_object_holder; for (std::vector::const_iterator l = effective_options.begin_macro_layout_files (); l != effective_options.end_macro_layout_files (); ++l) { - std::string lp = correct_path (*l, layout, base_path); + auto paths = correct_path (*l, layout, base_path, true); + for (auto lp = paths.begin (); lp != paths.end (); ++lp) { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + lp); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + *lp); - tl::InputStream macro_layout_stream (lp); - tl::log << tl::to_string (tr ("Reading")) << " " << lp; - db::Layout *new_layout = new db::Layout (false); - macro_layout_object_holder.push_back (new_layout); - macro_layouts.push_back (new_layout); + tl::InputStream macro_layout_stream (*lp); + tl::log << tl::to_string (tr ("Reading")) << " " << *lp; + db::Layout *new_layout = new db::Layout (false); + macro_layout_object_holder.push_back (new_layout); + macro_layouts.push_back (new_layout); - db::Reader reader (macro_layout_stream); - reader.read (*new_layout, options); + db::Reader reader (macro_layout_stream); + reader.read (*new_layout, options); + + if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) { + importer.warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), + *lp, new_layout->dbu (), layout.dbu ())); + } - if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) { - importer.warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), - lp, new_layout->dbu (), layout.dbu ())); } } diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc index 7debc3591..a3b755db0 100644 --- a/src/tl/tl/tlFileUtils.cc +++ b/src/tl/tl/tlFileUtils.cc @@ -25,6 +25,7 @@ #include "tlLog.h" #include "tlInternational.h" #include "tlEnv.h" +#include "tlGlobPattern.h" #include #include @@ -430,6 +431,53 @@ std::vector dir_entries (const std::string &s, bool with_files, boo return ee; } +static void glob_partial (const std::string &where, std::vector::const_iterator pfrom, std::vector::const_iterator pto, std::vector &res) +{ + if (pfrom == pto) { + if (! is_dir (where)) { + res.push_back (where); + } + return; + } + + auto p = where + *pfrom; + if (file_exists (p)) { + glob_partial (p, pfrom + 1, pto, res); + return; + } + + if (tl::trimmed_part (*pfrom) == "**") { + if (pfrom + 1 == pto) { + // a glob pattern can't be "**" without anything after that + return; + } + auto subdirs = dir_entries (where, false, true, true); + for (auto s = subdirs.begin (); s != subdirs.end (); ++s) { + glob_partial (combine_path (where, *s), pfrom, pto, res); + } + ++pfrom; + } + + tl::GlobPattern glob (tl::trimmed_part (*pfrom)); + ++pfrom; + auto entries = dir_entries (where, true, true, true); + for (auto e = entries.begin (); e != entries.end (); ++e) { + if (glob.match (*e)) { + glob_partial (combine_path (where, *e), pfrom, pto, res); + } + } +} + +std::vector glob_expand (const std::string &path) +{ + auto apath = absolute_file_path (path); + auto parts = split_path (apath); + + std::vector res; + glob_partial (std::string (), parts.begin (), parts.end (), res); + return res; +} + bool mkdir (const std::string &path) { #if defined(_WIN32) diff --git a/src/tl/tl/tlFileUtils.h b/src/tl/tl/tlFileUtils.h index 3456eb6da..4401ac41b 100644 --- a/src/tl/tl/tlFileUtils.h +++ b/src/tl/tl/tlFileUtils.h @@ -138,6 +138,14 @@ bool TL_PUBLIC is_dir (const std::string &s); */ std::vector TL_PUBLIC dir_entries (const std::string &s, bool with_files = true, bool with_dirs = true, bool without_dotfiles = false); +/** + * @brief Expands a glob pattern into a set of files + * + * This version supports "**" for recursive directory expansion. + * Apart from that the features of tl::GlobPattern are supported. + */ +std::vector TL_PUBLIC glob_expand (const std::string &path); + /** * @brief Rename the given file */ diff --git a/src/tl/unit_tests/tlFileUtilsTests.cc b/src/tl/unit_tests/tlFileUtilsTests.cc index c12776608..2eafe8bc3 100644 --- a/src/tl/unit_tests/tlFileUtilsTests.cc +++ b/src/tl/unit_tests/tlFileUtilsTests.cc @@ -995,3 +995,94 @@ TEST (24) EXPECT_EQ (tl::file_exists (p), false); } +// glob_expand +TEST (25) +{ + tl::TemporaryDirectory tmpdir ("tl_tests"); + auto p = tmpdir.path (); + + auto ad = tl::combine_path (p, "a"); + tl::mkpath (ad); + auto aad = tl::combine_path (ad, "a"); + tl::mkpath (aad); + auto aaad = tl::combine_path (aad, "a"); + tl::mkpath (aaad); + auto bd = tl::combine_path (p, "b"); + tl::mkpath (bd); + + { + std::ofstream os (tl::combine_path (ad, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (aad, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (aaad, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (aaad, "test2.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (bd, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (p, "test2.txt")); + os << "A test"; + os.close (); + } + + std::vector au; + + auto res = tl::glob_expand (tl::combine_path (p, "*.txt")); + au.push_back (tl::combine_path (p, "test2.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (res == au, true); + + res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*.txt")); + au.clear (); + au.push_back (tl::combine_path (p, "test2.txt")); + au.push_back (tl::combine_path (ad, "test.txt")); + au.push_back (tl::combine_path (aad, "test.txt")); + au.push_back (tl::combine_path (aaad, "test.txt")); + au.push_back (tl::combine_path (aaad, "test2.txt")); + au.push_back (tl::combine_path (bd, "test.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (res == au, true); + + res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*2.txt")); + au.clear (); + au.push_back (tl::combine_path (p, "test2.txt")); + au.push_back (tl::combine_path (aaad, "test2.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (res == au, true); + + res = tl::glob_expand (tl::combine_path (tl::combine_path (tl::combine_path (p, "**"), "a"), "*2.txt")); + au.clear (); + au.push_back (tl::combine_path (aaad, "test2.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (res == au, true); +} + From 41e9cb5893a60c0b14b8ecb67af16f91070c36b6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 7 Apr 2025 00:53:43 +0200 Subject: [PATCH 15/76] Maybe fixing basic issues with strmxor 1. Output of shape countsi in deep mode was hierarchical with output file, flat without 2. Refactoring of XOR (for_merged optimization) needed to create cover cell variants --- src/buddies/src/bd/strmxor.cc | 26 +-- src/db/db/dbArray.h | 11 ++ src/db/db/dbHierarchyBuilder.cc | 22 ++- src/db/db/dbHierarchyBuilder.h | 14 +- src/db/db/dbRecursiveShapeIterator.cc | 115 ++++++++----- src/db/db/dbRecursiveShapeIterator.h | 15 +- .../dbRecursiveShapeIteratorTests.cc | 153 ++++++++++++++++-- src/rdb/rdb/rdbUtils.cc | 2 +- 8 files changed, 280 insertions(+), 78 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 94ff7da1d..a18fc36cd 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -572,14 +572,22 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) if (! silent && ! no_summary) { if (result) { - tl::info << "No differences found"; + tl::info << tl::to_string (tr ("No differences found")); } else { const char *line_format = " %-10s %-12s %s"; - const char *sep = " -------------------------------------------------------"; - tl::info << "Result summary (layers without differences are not shown):" << tl::endl; - tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep; + std::string headline; + if (deep) { + headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (hierarchical shape count)"))); + } else { + headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (shape count)"))); + } + + const char *sep = " ----------------------------------------------------------------"; + + tl::info << tl::to_string (tr ("Result summary (layers without differences are not shown):")) << tl::endl; + tl::info << headline << tl::endl << sep; int ti = -1; for (std::map, ResultDescriptor>::const_iterator r = results.begin (); r != results.end (); ++r) { @@ -587,17 +595,17 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) if (r->first.first != ti) { ti = r->first.first; if (tolerances[ti] > db::epsilon) { - tl::info << tl::endl << "Tolerance " << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl; - tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep; + tl::info << tl::endl << tl::to_string (tr ("Tolerance ")) << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl; + tl::info << headline << tl::endl << sep; } } std::string out ("-"); std::string value; if (r->second.layer_a < 0 && ! dont_summarize_missing_layers) { - value = "(no such layer in first layout)"; + value = tl::to_string (tr ("(no such layer in first layout)")); } else if (r->second.layer_b < 0 && ! dont_summarize_missing_layers) { - value = "(no such layer in second layout)"; + value = tl::to_string (tr ("(no such layer in second layout)")); } else if (! r->second.is_empty ()) { if (r->second.layer_output >= 0 && r->second.layout) { out = r->second.layout->get_properties (r->second.layer_output).to_string (); @@ -857,7 +865,7 @@ bool run_deep_xor (const XORData &xor_data) result.layer_output = result.layout->insert_layer (lp); xor_res.insert_into (xor_data.output_layout, xor_data.output_cell, result.layer_output); } else { - result.shape_count = xor_res.count (); + result.shape_count = xor_res.hier_count (); } ++tol_index; diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index a4f51760f..ec8d16ef6 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -1571,6 +1571,17 @@ struct array_iterator } } + /** + * @brief Gets a value indicating whether the iterator is a synthetic one + * + * "is_singular" is true, if the iterator was default-created or with a single + * transformation. + */ + bool is_singular () const + { + return mp_base == 0; + } + private: trans_type m_trans; basic_array_iterator *mp_base; diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index ac3a31116..aa26e0adb 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -273,7 +273,7 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter) return; } - CellMapKey key (iter->top_cell ()->cell_index (), false, std::set ()); + CellMapKey key (iter->top_cell ()->cell_index (), false, std::set (), false); m_cm_entry = m_cell_map.find (key); if (m_cm_entry == m_cell_map.end ()) { @@ -351,7 +351,6 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co if (! key.clip_region.empty ()) { cn += "$CLIP_VAR"; description += "CLIP"; - } if (key.inactive) { cn += "$DIS"; @@ -360,6 +359,13 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co } description += "DISABLED"; } + if (key.skip_shapes) { + cn += "$SKIP"; + if (! description.empty ()) { + description += "/"; + } + description += "SKIPPED"; + } new_cell = mp_target->add_cell (cn.c_str ()); @@ -383,11 +389,11 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co } HierarchyBuilder::new_inst_mode -HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) +HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool skip_shapes) { if (all) { - CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set ()); + CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set (), skip_shapes); db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ())); // for new cells, create this instance @@ -402,7 +408,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } // To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array. - return (m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; + return (! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; } else { @@ -413,7 +419,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } bool -HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) +HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { if (all) { @@ -429,7 +435,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: return false; } - CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second); + CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second, skip_shapes); db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst_cell)); // for a new cell, create this instance @@ -441,7 +447,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: } } - return (m_cells_seen.find (key) == m_cells_seen.end ()); + return ! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end (); } } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 31a125f44..2bc2793d1 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -249,16 +249,16 @@ public: struct CellMapKey { CellMapKey () - : original_cell (0), inactive (false) + : original_cell (0), inactive (false), skip_shapes (false) { } - CellMapKey (db::cell_index_type _original_cell, bool _inactive, const std::set &_clip_region) - : original_cell (_original_cell), inactive (_inactive), clip_region (_clip_region) + CellMapKey (db::cell_index_type _original_cell, bool _inactive, const std::set &_clip_region, bool _skip_shapes) + : original_cell (_original_cell), inactive (_inactive), clip_region (_clip_region), skip_shapes (_skip_shapes) { } bool operator== (const CellMapKey &other) const { - return original_cell == other.original_cell && inactive == other.inactive && clip_region == other.clip_region; + return original_cell == other.original_cell && inactive == other.inactive && clip_region == other.clip_region && skip_shapes == other.skip_shapes; } bool operator< (const CellMapKey &other) const @@ -266,12 +266,14 @@ public: if (original_cell != other.original_cell) { return original_cell < other.original_cell; } if (inactive != other.inactive) { return inactive < other.inactive; } if (clip_region != other.clip_region) { return clip_region < other.clip_region; } + if (skip_shapes != other.skip_shapes) { return skip_shapes < other.skip_shapes; } return false; } db::cell_index_type original_cell; bool inactive; std::set clip_region; + bool skip_shapes; }; @@ -294,8 +296,8 @@ public: virtual void end (const RecursiveShapeIterator *iter); virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region); virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell); - virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all); - virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes); + virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes); virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region); /** diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index c92cd4bc4..b3965f9ce 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -74,6 +74,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_layer = d.m_layer; mp_cell = d.mp_cell; m_current_layer = d.m_current_layer; + m_skip_shapes = d.m_skip_shapes; + m_skip_shapes_member = d.m_skip_shapes_member; m_shape = d.m_shape; m_trans = d.m_trans; m_global_trans = d.m_global_trans; @@ -85,6 +87,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_local_complex_region_stack = d.m_local_complex_region_stack; m_local_region_stack = d.m_local_region_stack; m_skip_shapes_stack = d.m_skip_shapes_stack; + m_skip_shapes_member_stack = d.m_skip_shapes_member_stack; m_needs_reinit = d.m_needs_reinit; m_inst_quad_id = d.m_inst_quad_id; m_inst_quad_id_stack = d.m_inst_quad_id_stack; @@ -469,6 +472,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const m_local_region_stack.push_back (m_global_trans.inverted () * m_region); m_skip_shapes_stack.clear (); m_skip_shapes_stack.push_back (false); + m_skip_shapes_member_stack.clear (); + m_skip_shapes_member_stack.push_back (false); m_local_complex_region_stack.clear (); if (mp_complex_region.get ()) { @@ -814,39 +819,6 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const bool RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const { - bool skip_shapes = false; - - if (m_for_merged_input && ! m_skip_shapes_stack.back () && (! m_has_layers || m_layers.size () == 1)) { - - // Try some optimization: if the instance we're looking at is entirely covered - // by a rectangle (other objects are too expensive to check), then we skip it - // - // We check 10 shapes max. - - box_type inst_bx; - if (m_inst->size () == 1) { - inst_bx = m_inst->bbox (m_box_convert); - } else { - inst_bx = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ()); - } - - unsigned int l = m_has_layers ? m_layers.front () : m_layer; - auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); - size_t nmax = 10; - while (! si.at_end () && nmax-- > 0) { - if (inst_bx.inside (si->rectangle ())) { - skip_shapes = true; - break; - } - ++si; - } - - } - - if (skip_shapes && (! receiver || ! receiver->wants_all_cells ())) { - return false; - } - tl_assert (mp_layout); m_trans_stack.push_back (m_trans); @@ -874,7 +846,8 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const } m_local_region_stack.push_back (new_region); - m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes); + m_skip_shapes_stack.push_back (m_skip_shapes); + m_skip_shapes_member_stack.push_back (m_skip_shapes_member); if (! m_local_complex_region_stack.empty ()) { @@ -948,6 +921,8 @@ RecursiveShapeIterator::pop () const m_inst = m_inst_iterators.back (); m_inst_array = m_inst_array_iterators.back (); m_inst_quad_id = m_inst_quad_id_stack.back (); + m_skip_shapes = m_skip_shapes_stack.back (); + m_skip_shapes_member = m_skip_shapes_member_stack.back (); m_inst_iterators.pop_back (); m_inst_array_iterators.pop_back (); m_inst_quad_id_stack.pop_back (); @@ -958,6 +933,7 @@ RecursiveShapeIterator::pop () const m_cells.pop_back (); m_local_region_stack.pop_back (); m_skip_shapes_stack.pop_back (); + m_skip_shapes_member_stack.pop_back (); if (! m_local_complex_region_stack.empty ()) { m_local_complex_region_stack.pop_back (); } @@ -982,7 +958,7 @@ RecursiveShapeIterator::start_shapes () const void RecursiveShapeIterator::new_layer () const { - if (m_skip_shapes_stack.back () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { + if (skip_shapes () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { m_shape = shape_iterator (); } else if (! m_overlapping) { m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); @@ -1029,6 +1005,32 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const new_inst (receiver); } +bool +RecursiveShapeIterator::instance_is_covered (const box_type &inst_bx, unsigned int layer) const +{ + // Try some optimization: if the instance we're looking at is entirely covered + // by a rectangle (other objects are too expensive to check), then we skip it + // + // We check 10 shapes max. + + auto si = cell ()->shapes (layer).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); + size_t nmax = 10; + while (! si.at_end () && nmax-- > 0) { + if (inst_bx.inside (si->rectangle ())) { + return true; + } + ++si; + } + + return false; +} + +bool +RecursiveShapeIterator::skip_shapes () const +{ + return m_skip_shapes_stack.back () || m_skip_shapes_member_stack.back (); +} + void RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const { @@ -1055,9 +1057,19 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const all_of_instance = m_local_complex_region_stack.empty (); } + m_skip_shapes = skip_shapes (); + m_skip_shapes_member = false; + + if (m_for_merged_input && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) { + box_type inst_bx = m_inst->bbox (m_box_convert); + m_skip_shapes = instance_is_covered (inst_bx, m_has_layers ? m_layers.front () : m_layer); + } + RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all; if (receiver) { - ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); + ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance, m_skip_shapes); + } else if (m_skip_shapes) { + ni = RecursiveShapeReceiver::NI_skip; } if (ni == RecursiveShapeReceiver::NI_skip) { @@ -1095,7 +1107,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const // skip instance array members not part of the complex region while (! m_inst_array.at_end ()) { - db::Box ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ()); + box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ()); if (! is_outside_complex_region (ia_box)) { break; } else { @@ -1105,12 +1117,31 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const } - while (! m_inst_array.at_end () && receiver) { - if (receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) { - break; - } else { - ++m_inst_array; + m_skip_shapes_member = false; + + while (! m_inst_array.at_end () && (m_for_merged_input || receiver)) { + + m_skip_shapes_member = m_skip_shapes; + if (m_for_merged_input && ! m_inst_array.is_singular () && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) { + + box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ()); + m_skip_shapes_member = instance_is_covered (ia_box, m_has_layers ? m_layers.front () : m_layer); + } + + bool skip = false; + if (receiver) { + skip = ! receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance (), m_skip_shapes_member); + } else { + skip = m_skip_shapes_member; + } + + if (skip) { + ++m_inst_array; + } else { + break; + } + } } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 26ee32b7e..b0e685686 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -868,6 +868,7 @@ private: mutable unsigned int m_layer; mutable const cell_type *mp_cell; mutable size_t m_current_layer; + mutable bool m_skip_shapes, m_skip_shapes_member; mutable shape_iterator m_shape; mutable cplx_trans_type m_trans; mutable std::vector m_trans_stack; @@ -876,7 +877,7 @@ private: mutable std::vector m_cells; mutable std::vector m_local_complex_region_stack; mutable std::vector m_local_region_stack; - mutable std::vector m_skip_shapes_stack; + mutable std::vector m_skip_shapes_stack, m_skip_shapes_member_stack; mutable bool m_needs_reinit; mutable size_t m_inst_quad_id; mutable std::vector m_inst_quad_id_stack; @@ -899,6 +900,8 @@ private: bool down (RecursiveShapeReceiver *receiver) const; void pop () const; + bool instance_is_covered (const box_type &inst_bx, unsigned int layer) const; + bool skip_shapes () const; bool is_outside_complex_region (const db::Box &box) const; void set_inactive (bool a) const @@ -1013,8 +1016,11 @@ public: * - NI_all: iterate all members through "new_inst_member" * - NI_single: iterate a single member (the first one) * - NI_skip: skips the whole array (not a single instance is iterated) + * + * The "skip_shapes" parameter indicates that the instance is visited with the + * purpose of skipping all shapes. This is used to implement the "for_merged" optimization. */ - virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; } + virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return NI_all; } /** * @brief Enters a new array member of the instance @@ -1026,8 +1032,11 @@ public: * "all" is true, if an instance array is iterated in "all" mode (see new_inst). * * If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered. + * + * The "skip_shapes" parameter indicates that the instance member is visited with the + * purpose of skipping all shapes. This is used to implement the "for_merged" optimization. */ - virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } + virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return true; } /** * @brief Delivers a shape diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 7c386f011..c7205fbf3 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -756,15 +756,25 @@ namespace { : public db::RecursiveShapeReceiver { public: - FlatPusher (std::set *boxes) : mp_boxes (boxes) { } + FlatPusher (std::set *boxes = 0) : mp_boxes (boxes ? boxes : &m_boxes) { } void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { mp_boxes->insert (trans * shape.bbox ()); } + std::string to_string () const + { + std::vector s; + for (auto i = mp_boxes->begin (); i != mp_boxes->end (); ++i) { + s.push_back (i->to_string ()); + } + return tl::join (s.begin (), s.end (), ";"); + } + private: std::set *mp_boxes; + std::set m_boxes; }; } @@ -1038,7 +1048,7 @@ public: m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/) { m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); if (all) { @@ -1048,7 +1058,7 @@ public: return NI_all; } - virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/) { m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (always_apply * trans); if (all) { @@ -1073,9 +1083,9 @@ class ReceiverRejectingACellInstanceArray public: ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all); + LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes); return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip; } @@ -1089,9 +1099,9 @@ class ReceiverRejectingACellInstanceArrayExceptOne public: ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all); + LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes); return inst.object ().cell_index () != m_rejected ? NI_all : NI_single; } @@ -1105,9 +1115,9 @@ class ReceiverRejectingACellInstance public: ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { } - virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all); + LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all, skip_shapes); return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected; } @@ -1586,49 +1596,174 @@ TEST(12_ForMerged) db::RecursiveShapeIterator i1 (*g, c0, 0); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); std::vector lv; lv.push_back (0); i1 = db::RecursiveShapeIterator (*g, c0, lv); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); lv.push_back (1); // empty, but kills "for merged" optimization i1 = db::RecursiveShapeIterator (*g, c0, lv); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)"); + } i1.set_for_merged_input (true); x = collect(i1, *g); // no longer optimized EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)"); + } i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-100, 0, 100, 50)); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100)"); + } + i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)"); + } i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-101, 0, 100, 50)); i1.set_overlapping (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)"); + } i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)"); + } } +TEST(12b_ForMerged) +{ + std::unique_ptr g (new db::Layout ()); + g->insert_layer (0); + g->insert_layer (1); + db::Cell &c0 (g->cell (g->add_cell ())); + db::Cell &c1 (g->cell (g->add_cell ())); + + db::Box b (0, 100, 1000, 1200); + c0.shapes (0).insert (db::Box (0, 0, 3000, 2200)); + c1.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt)); + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (2000, 1000)), db::Vector (0, 2000), db::Vector (2000, 0), 2l, 2l)); + + std::string x; + + db::RecursiveShapeIterator i1 (*g, c0, 0); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } + + i1.set_for_merged_input (false); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 2000)))); + + db::RecursiveShapeIterator i2 (*g, c0, 0); + + x = collect(i2, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)"); + EXPECT_EQ (collect_with_copy(i2, *g), x); + + { + FlatPusher f; + i2.reset (); + i2.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } + + i2.set_for_merged_input (true); + x = collect(i2, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)"); + EXPECT_EQ (collect_with_copy(i2, *g), x); + + { + FlatPusher f; + i2.reset (); + i2.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } +} TEST(13_ForMergedPerformance) { diff --git a/src/rdb/rdb/rdbUtils.cc b/src/rdb/rdb/rdbUtils.cc index 6d0181162..4692b35bd 100644 --- a/src/rdb/rdb/rdbUtils.cc +++ b/src/rdb/rdb/rdbUtils.cc @@ -146,7 +146,7 @@ public: m_cell_stack.pop_back (); } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { db::cell_index_type ci = inst.object ().cell_index (); if (m_id_to_cell.find (ci) != m_id_to_cell.end ()) { From 789e183be9708d46e301aa465642c6fa41351597 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 00:04:21 +0200 Subject: [PATCH 16/76] Shortcutting hierarchy in case of skipped shapes, this restores the original performance --- src/db/db/dbHierarchyBuilder.cc | 30 ++++++++++++++++-------------- src/db/db/dbHierarchyBuilder.h | 10 ++++------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index aa26e0adb..bf68802fc 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -273,7 +273,7 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter) return; } - CellMapKey key (iter->top_cell ()->cell_index (), false, std::set (), false); + CellMapKey key (iter->top_cell ()->cell_index (), false, std::set ()); m_cm_entry = m_cell_map.find (key); if (m_cm_entry == m_cell_map.end ()) { @@ -359,13 +359,6 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co } description += "DISABLED"; } - if (key.skip_shapes) { - cn += "$SKIP"; - if (! description.empty ()) { - description += "/"; - } - description += "SKIPPED"; - } new_cell = mp_target->add_cell (cn.c_str ()); @@ -391,9 +384,14 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co HierarchyBuilder::new_inst_mode HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool skip_shapes) { - if (all) { + if (skip_shapes) { - CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set (), skip_shapes); + // don't consider this instance if all cells are skipped + return NI_skip; + + } else if (all) { + + CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set ()); db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ())); // for new cells, create this instance @@ -408,7 +406,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } // To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array. - return (! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; + return m_cells_seen.find (key) == m_cells_seen.end () ? NI_single : NI_skip; } else { @@ -421,7 +419,11 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn bool HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - if (all) { + if (skip_shapes) { + + return false; + + } else if (all) { return true; @@ -435,7 +437,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: return false; } - CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second, skip_shapes); + CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second); db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst_cell)); // for a new cell, create this instance @@ -447,7 +449,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: } } - return ! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end (); + return m_cells_seen.find (key) == m_cells_seen.end (); } } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 2bc2793d1..d1d056718 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -249,16 +249,16 @@ public: struct CellMapKey { CellMapKey () - : original_cell (0), inactive (false), skip_shapes (false) + : original_cell (0), inactive (false) { } - CellMapKey (db::cell_index_type _original_cell, bool _inactive, const std::set &_clip_region, bool _skip_shapes) - : original_cell (_original_cell), inactive (_inactive), clip_region (_clip_region), skip_shapes (_skip_shapes) + CellMapKey (db::cell_index_type _original_cell, bool _inactive, const std::set &_clip_region) + : original_cell (_original_cell), inactive (_inactive), clip_region (_clip_region) { } bool operator== (const CellMapKey &other) const { - return original_cell == other.original_cell && inactive == other.inactive && clip_region == other.clip_region && skip_shapes == other.skip_shapes; + return original_cell == other.original_cell && inactive == other.inactive && clip_region == other.clip_region; } bool operator< (const CellMapKey &other) const @@ -266,14 +266,12 @@ public: if (original_cell != other.original_cell) { return original_cell < other.original_cell; } if (inactive != other.inactive) { return inactive < other.inactive; } if (clip_region != other.clip_region) { return clip_region < other.clip_region; } - if (skip_shapes != other.skip_shapes) { return skip_shapes < other.skip_shapes; } return false; } db::cell_index_type original_cell; bool inactive; std::set clip_region; - bool skip_shapes; }; From 8150e732af03365792ee2bc8c26b36b01db169b7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 19:14:01 +0200 Subject: [PATCH 17/76] Hopefully fixing strm2xor finally, added a test --- src/buddies/unit_tests/bdStrmxorTests.cc | 57 +++++++++++++++++++---- src/db/db/dbHierarchyBuilder.cc | 26 ++++------- testdata/bd/strmxor_au7d.oas | Bin 0 -> 522 bytes testdata/bd/strmxor_covered1.gds | Bin 0 -> 1152 bytes testdata/bd/strmxor_covered2.gds | Bin 0 -> 1296 bytes 5 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 testdata/bd/strmxor_au7d.oas create mode 100644 testdata/bd/strmxor_covered1.gds create mode 100644 testdata/bd/strmxor_covered2.gds diff --git a/src/buddies/unit_tests/bdStrmxorTests.cc b/src/buddies/unit_tests/bdStrmxorTests.cc index cde4afde0..c065bbb43 100644 --- a/src/buddies/unit_tests/bdStrmxorTests.cc +++ b/src/buddies/unit_tests/bdStrmxorTests.cc @@ -105,7 +105,7 @@ TEST(1A_Flat) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 3/0 30\n" " 6/0 6/0 41\n" " 8/1 8/1 1\n" @@ -146,8 +146,8 @@ TEST(1A_Deep) "Layer 10/0 is not present in first layout, but in second\n" "Result summary (layers without differences are not shown):\n" "\n" - " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " Layer Output Differences (hierarchical shape count)\n" + " ----------------------------------------------------------------\n" " 3/0 3/0 3\n" " 6/0 6/0 314\n" " 8/1 8/1 1\n" @@ -177,7 +177,7 @@ TEST(1B_Flat) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 - 30\n" " 6/0 - 41\n" " 8/1 - 1\n" @@ -206,9 +206,9 @@ TEST(1B_Deep) "Layer 10/0 is not present in first layout, but in second\n" "Result summary (layers without differences are not shown):\n" "\n" - " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" - " 3/0 - 30\n" + " Layer Output Differences (hierarchical shape count)\n" + " ----------------------------------------------------------------\n" + " 3/0 - 3\n" " 6/0 - 314\n" " 8/1 - 1\n" " 10/0 - (no such layer in first layout)\n" @@ -417,7 +417,7 @@ TEST(3_FlatCount) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 - 31\n" " 6/0 - 217\n" " 8/1 - 168\n" @@ -483,7 +483,7 @@ TEST(3_FlatCountHeal) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 - 30\n" " 6/0 - 41\n" " 8/1 - 1\n" @@ -756,3 +756,42 @@ TEST(6_Deep) "Layer 10/0 is not present in first layout, but in second\n" ); } + +TEST(7_OptimizeDeep) +{ + tl::CaptureChannel cap; + + std::string input_a = tl::testdata (); + input_a += "/bd/strmxor_covered1.gds"; + + std::string input_b = tl::testdata (); + input_b += "/bd/strmxor_covered2.gds"; + + std::string au = tl::testdata (); + au += "/bd/strmxor_au7d.oas"; + + std::string output = this->tmp_file ("tmp.oas"); + + const char *argv[] = { "x", "-u", input_a.c_str (), input_b.c_str (), output.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons)); + EXPECT_EQ (cap.captured_text (), + "Result summary (layers without differences are not shown):\n" + "\n" + " Layer Output Differences (hierarchical shape count)\n" + " ----------------------------------------------------------------\n" + " 2/0 2/0 1\n" + " 3/0 3/0 8\n" + "\n" + ); +} diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index bf68802fc..b54354858 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -152,15 +152,17 @@ static std::pair > compute_clip_variant (const db::Box & } HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe) - : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans) + : mp_target (target), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans) { set_shape_receiver (pipe); + reset (); } HierarchyBuilder::HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe) - : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (0), m_wants_all_cells (false), m_trans (trans) + : mp_target (target), m_target_layer (0), m_wants_all_cells (false), m_trans (trans) { set_shape_receiver (pipe); + reset (); } HierarchyBuilder::~HierarchyBuilder () @@ -178,6 +180,8 @@ void HierarchyBuilder::reset () { m_initial_pass = true; + m_cm_new_entry = false; + mp_initial_cell = 0; m_cells_to_be_filled.clear (); @@ -186,7 +190,6 @@ HierarchyBuilder::reset () m_cells_seen.clear (); m_cell_stack.clear (); m_cm_entry = null_iterator; - m_cm_new_entry = false; } const std::pair & @@ -384,12 +387,7 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co HierarchyBuilder::new_inst_mode HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool skip_shapes) { - if (skip_shapes) { - - // don't consider this instance if all cells are skipped - return NI_skip; - - } else if (all) { + if (all) { CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set ()); db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ())); @@ -406,7 +404,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } // To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array. - return m_cells_seen.find (key) == m_cells_seen.end () ? NI_single : NI_skip; + return (! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; } else { @@ -419,11 +417,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn bool HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - if (skip_shapes) { - - return false; - - } else if (all) { + if (all) { return true; @@ -449,7 +443,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: } } - return m_cells_seen.find (key) == m_cells_seen.end (); + return ! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end (); } } diff --git a/testdata/bd/strmxor_au7d.oas b/testdata/bd/strmxor_au7d.oas new file mode 100644 index 0000000000000000000000000000000000000000..9d64f3f3893f25cab4cb296864bba870aa54b2f8 GIT binary patch literal 522 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqfnXwp1GBk`Zz`y_i$~x7q literal 0 HcmV?d00001 diff --git a/testdata/bd/strmxor_covered1.gds b/testdata/bd/strmxor_covered1.gds new file mode 100644 index 0000000000000000000000000000000000000000..f3d6eb6c4a02dd7bc3abe9987e76a9e581f1a214 GIT binary patch literal 1152 zcmb7@y-EW?6ot?JO%|ddCW;`7f{j4}mZC)@KPaN&4?!el9w854AvS_f5G~Rrg|&#K zg_SR&m4%fgjAwS4I9Zi-7lv=SyZ794XYN42w$Bh)cIFi>d;nRjnD6jyvAr$y9dIcrrfpadIh*R`yd&kXEak{o1e_t)y{3*U!kzif zw&{85|w1Fkl69|4cXBI>weR6m2HF1a^umR%N9zhc3n z*i_)vTBLgXqUzp4kazg`g{%34q0@hByzwwTsWHh=BiU_r?3TV0+%ERGuXjLR z1K7F*4hEV!t~ha5j9CYkZ-L@@#CicZ{s1&}oDAzHOph-_^YCaqKb)k5`Pn>?Qu1!b JsB5_{z5pQMw(_j#Xl-uH!p>uw_FxJ{4n5ddgGe|(O>HA@TAK$n*( zOdX9a-d{eC&)$q)oNX16%l!SHF=htvDQQ|=I51h4>f3FewUrTTtAP-3_pSXbD6O@7 zfYYmt*7cmwt8iQ^RYwTFy%x`{i+38utejFi%XlwJ&Z*^`D5G^fC-mse-%zSf=sh0K zi^r?(&`+6ThyLGrm$w?RD|5cYH)UVX3H@6j!|jNwBaH6XgtS*@uKZ@cdWXEf>2oBf zxyI3+^SJ%MQT)a5X8+cBVsU&>qn@Egy4$HAxAdLjcHn|}xB?Q4xlPv? z%mUpdAi;(-B!2!l@)2W08uNF|Gjoz)LmG1jK+iCcV8a@DW=}L?3^fwHJ0iVXon`Hp zguvt>uv}K^y0ESnwKw=bFmw)dvd4up9l+Xajk->T@l&cNFs8V&`cprwq?G!ZSZ6P? KmlM=jzK<_V<@z=N literal 0 HcmV?d00001 From e8d796aded4670a1c47badfa70323d8bfbf7770d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 19:38:56 +0200 Subject: [PATCH 18/76] Fixed unit tests --- testdata/buddies/buddies.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/buddies/buddies.rb b/testdata/buddies/buddies.rb index bf7afb096..7e309ac27 100644 --- a/testdata/buddies/buddies.rb +++ b/testdata/buddies/buddies.rb @@ -113,7 +113,7 @@ Warning: Layer 2/0 is not present in first layout, but in second Result summary (layers without differences are not shown): Layer Output Differences (shape count) - ------------------------------------------------------- + ---------------------------------------------------------------- 1/0 - (no such layer in second layout) 2/0 - (no such layer in first layout) From 3a752fd2c7813ba0f25a768d4b03e35bf7ca0339 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 19:55:50 +0200 Subject: [PATCH 19/76] Adding 'total' time for -d11 in all scripts --- src/buddies/src/bd/strmclip.cc | 3 +++ src/buddies/src/bd/strmcmp.cc | 3 +++ src/buddies/src/bd/strmrun.cc | 4 ++++ src/buddies/src/bd/strmxor.cc | 3 +++ 4 files changed, 13 insertions(+) diff --git a/src/buddies/src/bd/strmclip.cc b/src/buddies/src/bd/strmclip.cc index ca36b2507..917b5a24d 100644 --- a/src/buddies/src/bd/strmclip.cc +++ b/src/buddies/src/bd/strmclip.cc @@ -29,6 +29,7 @@ #include "dbSaveLayoutOptions.h" #include "tlLog.h" #include "tlCommandLineParser.h" +#include "tlTimer.h" struct ClipData @@ -200,6 +201,8 @@ BD_PUBLIC int strmclip (int argc, char *argv[]) cmd.parse (argc, argv); + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + clip (data); return 0; diff --git a/src/buddies/src/bd/strmcmp.cc b/src/buddies/src/bd/strmcmp.cc index 70cc712e9..ccc31c55c 100644 --- a/src/buddies/src/bd/strmcmp.cc +++ b/src/buddies/src/bd/strmcmp.cc @@ -25,6 +25,7 @@ #include "dbLayoutDiff.h" #include "dbReader.h" #include "tlCommandLineParser.h" +#include "tlTimer.h" BD_PUBLIC int strmcmp (int argc, char *argv[]) { @@ -141,6 +142,8 @@ BD_PUBLIC int strmcmp (int argc, char *argv[]) throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given"); } + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + db::Layout layout_a; db::Layout layout_b; diff --git a/src/buddies/src/bd/strmrun.cc b/src/buddies/src/bd/strmrun.cc index fa15cf018..d13abefe7 100644 --- a/src/buddies/src/bd/strmrun.cc +++ b/src/buddies/src/bd/strmrun.cc @@ -28,6 +28,7 @@ #include "tlLog.h" #include "tlCommandLineParser.h" #include "tlFileUtils.h" +#include "tlTimer.h" #include "rba.h" #include "pya.h" #include "gsi.h" @@ -97,5 +98,8 @@ BD_PUBLIC int strmrun (int argc, char *argv[]) lym::Macro macro; macro.load_from (script); macro.set_file_path (script); + + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + return macro.run (); } diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index a18fc36cd..645ae9a2c 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -32,6 +32,7 @@ #include "gsiExpression.h" #include "tlCommandLineParser.h" #include "tlThreads.h" +#include "tlTimer.h" namespace { @@ -455,6 +456,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) } } + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + db::Layout layout_a; db::Layout layout_b; From 0542ef835ae6015dded16dde4704d29308d1cbe6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 21:14:19 +0200 Subject: [PATCH 20/76] strm2xor: parallelize by layer and not internally with -u --- src/buddies/src/bd/strmxor.cc | 267 ++++++++++++++++++++++------------ 1 file changed, 173 insertions(+), 94 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 645ae9a2c..26cf4e3f9 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -32,6 +32,7 @@ #include "gsiExpression.h" #include "tlCommandLineParser.h" #include "tlThreads.h" +#include "tlThreadedWorkers.h" #include "tlTimer.h" namespace { @@ -320,7 +321,8 @@ struct XORData dont_summarize_missing_layers (false), silent (false), no_summary (false), threads (0), tile_size (0.0), heal_results (false), - output_layout (0), output_cell (0) + output_layout (0), output_cell (0), + layers_missing (0) { } db::Layout *layout_a, *layout_b; @@ -337,6 +339,8 @@ struct XORData db::cell_index_type output_cell; std::map, db::LPLogicalLessFunc> l2l_map; std::map, ResultDescriptor> *results; + mutable int layers_missing; + mutable tl::Mutex lock; }; } @@ -769,15 +773,170 @@ bool run_tiled_xor (const XORData &xor_data) return result; } -bool run_deep_xor (const XORData &xor_data) -{ - db::DeepShapeStore dss; - dss.set_threads (xor_data.threads); +class XORJob + : public tl::JobBase +{ +public: + XORJob (int nworkers) + : tl::JobBase (nworkers) + { + } + + virtual tl::Worker *create_worker (); +}; + +class XORWorker + : public tl::Worker +{ +public: + XORWorker (XORJob *job); + void perform_task (tl::Task *task); + + db::DeepShapeStore &dss () + { + return m_dss; + } + +private: + XORJob *mp_job; + db::DeepShapeStore m_dss; +}; + +class XORTask + : public tl::Task +{ +public: + XORTask (const XORData *xor_data, const db::LayerProperties &layer_props, int la, int lb, double dbu) + : mp_xor_data (xor_data), m_layer_props (layer_props), m_la (la), m_lb (lb), m_dbu (dbu) + { + // .. nothing yet .. + } + + void run (XORWorker *worker) const + { + if ((m_la < 0 || m_lb < 0) && ! mp_xor_data->dont_summarize_missing_layers) { + + if (m_la < 0) { + (mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in first layout, but in second"; + } else { + (mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in second layout, but in first"; + } + + tl::MutexLocker locker (&mp_xor_data->lock); + + mp_xor_data->layers_missing += 1; + + int tol_index = 0; + for (std::vector::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) { + + ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second; + result.layer_a = m_la; + result.layer_b = m_lb; + result.layout = mp_xor_data->output_layout; + result.top_cell = mp_xor_data->output_cell; + + ++tol_index; + + } + + } else { + + tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + m_layer_props.to_string ()); + + db::RecursiveShapeIterator ri_a, ri_b; + + if (m_la >= 0) { + ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la); + ri_a.set_for_merged_input (true); + } + + if (m_lb >= 0) { + ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb); + ri_b.set_for_merged_input (true); + } + + db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu)); + db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu)); + + db::Region xor_res; + { + tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + m_layer_props.to_string ()); + xor_res = in_a ^ in_b; + } + + int tol_index = 0; + for (std::vector::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) { + + db::LayerProperties lp = m_layer_props; + if (lp.layer >= 0) { + lp.layer += tol_index * mp_xor_data->tolerance_bump; + } + + if (*t > db::epsilon) { + tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + m_layer_props.to_string ()); + xor_res.size (-db::coord_traits::rounded (0.5 * *t / m_dbu)); + xor_res.size (db::coord_traits::rounded (0.5 * *t / m_dbu)); + } + + { + tl::MutexLocker locker (&mp_xor_data->lock); + + ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second; + result.layer_a = m_la; + result.layer_b = m_lb; + result.layout = mp_xor_data->output_layout; + result.top_cell = mp_xor_data->output_cell; + + if (mp_xor_data->output_layout) { + result.layer_output = result.layout->insert_layer (lp); + xor_res.insert_into (mp_xor_data->output_layout, mp_xor_data->output_cell, result.layer_output); + } else { + result.shape_count = xor_res.hier_count (); + } + } + + ++tol_index; + + } + + } + } + +private: + const XORData *mp_xor_data; + const db::LayerProperties &m_layer_props; + int m_la; + int m_lb; + double m_dbu; +}; + +XORWorker::XORWorker (XORJob *job) + : tl::Worker (), mp_job (job) +{ // TODO: this conflicts with the "set_for_merged_input" optimization below. // It seems not to be very effective then. Why? - dss.set_wants_all_cells (true); // saves time for less cell mapping operations + m_dss.set_wants_all_cells (true); // saves time for less cell mapping operations +} +void +XORWorker::perform_task (tl::Task *task) +{ + XORTask *xor_task = dynamic_cast (task); + if (xor_task) { + xor_task->run (this); + } +} + +tl::Worker * +XORJob::create_worker () +{ + return new XORWorker (this); +} + + +bool run_deep_xor (const XORData &xor_data) +{ double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ()); if (tl::verbosity () >= 20) { @@ -790,98 +949,18 @@ bool run_deep_xor (const XORData &xor_data) xor_data.output_layout->dbu (dbu); } - bool result = true; - - int index = 1; + XORJob job (xor_data.threads); for (std::map >::const_iterator ll = xor_data.l2l_map.begin (); ll != xor_data.l2l_map.end (); ++ll) { - - if ((ll->second.first < 0 || ll->second.second < 0) && ! xor_data.dont_summarize_missing_layers) { - - if (ll->second.first < 0) { - (xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in first layout, but in second"; - } else { - (xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in second layout, but in first"; - } - - result = false; - - int tol_index = 0; - for (std::vector::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) { - - ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second; - result.layer_a = ll->second.first; - result.layer_b = ll->second.second; - result.layout = xor_data.output_layout; - result.top_cell = xor_data.output_cell; - - ++tol_index; - - } - - } else { - - tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + ll->first.to_string ()); - - db::RecursiveShapeIterator ri_a, ri_b; - - if (ll->second.first >= 0) { - ri_a = db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first); - ri_a.set_for_merged_input (true); - } - - if (ll->second.second >= 0) { - ri_b = db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second); - ri_b.set_for_merged_input (true); - } - - db::Region in_a (ri_a, dss, db::ICplxTrans (xor_data.layout_a->dbu () / dbu)); - db::Region in_b (ri_b, dss, db::ICplxTrans (xor_data.layout_b->dbu () / dbu)); - - db::Region xor_res; - { - tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + ll->first.to_string ()); - xor_res = in_a ^ in_b; - } - - int tol_index = 0; - for (std::vector::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) { - - db::LayerProperties lp = ll->first; - if (lp.layer >= 0) { - lp.layer += tol_index * xor_data.tolerance_bump; - } - - ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second; - result.layer_a = ll->second.first; - result.layer_b = ll->second.second; - result.layout = xor_data.output_layout; - result.top_cell = xor_data.output_cell; - - if (*t > db::epsilon) { - tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + ll->first.to_string ()); - xor_res.size (-db::coord_traits::rounded (0.5 * *t / dbu)); - xor_res.size (db::coord_traits::rounded (0.5 * *t / dbu)); - } - - if (xor_data.output_layout) { - result.layer_output = result.layout->insert_layer (lp); - xor_res.insert_into (xor_data.output_layout, xor_data.output_cell, result.layer_output); - } else { - result.shape_count = xor_res.hier_count (); - } - - ++tol_index; - - } - - } - - ++index; - + job.schedule (new XORTask (&xor_data, ll->first, ll->second.first, ll->second.second, dbu)); } - // Determines the output status + job.start (); + job.wait (); + + // Determine the output status + + bool result = (xor_data.layers_missing == 0); for (std::map, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end () && result; ++r) { result = r->second.is_empty (); } From a27fd3e0beaca124f0b016b0ece5e90859d10271 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 22:40:04 +0200 Subject: [PATCH 21/76] Drop OASIS warning about ghost cells and print a DEF reader warning if a foreign cell cannot be substituted --- src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc | 8 ++++++++ src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index c8d3d840c..e86f79ce3 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -276,6 +276,14 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti } + // Warn about cells that could not be resolved + for (std::map::iterator f = foreign_cells.begin (); f != foreign_cells.end (); ++f) { + if (f->second != seen) { + importer.warn (tl::sprintf (tl::to_string (tr ("Could not find a substitution layout for foreign cell '%s'")), + f->first)); + } + } + } state.finish (layout); diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 6a7f5afee..548a4e447 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1671,7 +1671,6 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save // skip cell body if the cell is not to be written if (skip_cell_body (cref)) { - tl::warn << tl::to_string (tr ("Cannot write ghost cell to OASIS - skipping cell: ")) << layout.cell_name (*cell); continue; } From 0ec8e181739a5303cb68be2f76014c6b90c6a977 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 23:52:50 +0200 Subject: [PATCH 22/76] refining the DEF warning on missing foreign cell --- src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index e86f79ce3..06e42a0c7 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -278,7 +278,7 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti // Warn about cells that could not be resolved for (std::map::iterator f = foreign_cells.begin (); f != foreign_cells.end (); ++f) { - if (f->second != seen) { + if (f->second != seen && layout.cell (f->second).is_ghost_cell ()) { importer.warn (tl::sprintf (tl::to_string (tr ("Could not find a substitution layout for foreign cell '%s'")), f->first)); } From 09329442f0781a180b0145d6bb7a889c50f5a70a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 9 Apr 2025 23:30:13 +0200 Subject: [PATCH 23/76] Fixing issue #2019 (build issue against Qt 6.9) --- scripts/mkqtdecl6/mkqtdecl.conf | 3 +- src/gsiqt/qt6/QtXml/gsiDeclQDomNodeList.cc | 49 ++++------------------ 2 files changed, 11 insertions(+), 41 deletions(-) diff --git a/scripts/mkqtdecl6/mkqtdecl.conf b/scripts/mkqtdecl6/mkqtdecl.conf index 427141e2b..4b7c15fa5 100644 --- a/scripts/mkqtdecl6/mkqtdecl.conf +++ b/scripts/mkqtdecl6/mkqtdecl.conf @@ -547,7 +547,7 @@ drop_method "QDebug", /QDebug::operator\s*<<\((?!const\s+QString\s*&)/ # don't m drop_method "", /::operator\s*<<\(QDebug\s*\w*\s*,\s*(?!const\s+QString\s*&)/ # don't map the others right now - too many (TODO: how to map?) drop_method "QNoDebug", /QNoDebug::operator<add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2484 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QDomNodeList &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QDomNodeList *)cls)->operator!= (arg1)); -} - - // QDomNodeList &QDomNodeList::operator=(const QDomNodeList &) @@ -191,25 +178,6 @@ static void _call_f_operator_eq__2484 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QDomNodeList::operator==(const QDomNodeList &) - - -static void _init_f_operator_eq__eq__c2484 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("arg1"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2484 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QDomNodeList &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QDomNodeList *)cls)->operator== (arg1)); -} - - // int QDomNodeList::size() @@ -238,14 +206,15 @@ static gsi::Methods methods_QDomNodeList () { methods += new qt_gsi::GenericMethod ("isEmpty?", "@brief Method bool QDomNodeList::isEmpty()\n", true, &_init_f_isEmpty_c0, &_call_f_isEmpty_c0); methods += new qt_gsi::GenericMethod ("item", "@brief Method QDomNode QDomNodeList::item(int index)\n", true, &_init_f_item_c767, &_call_f_item_c767); methods += new qt_gsi::GenericMethod ("length", "@brief Method int QDomNodeList::length()\n", true, &_init_f_length_c0, &_call_f_length_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QDomNodeList::operator!=(const QDomNodeList &)\n", true, &_init_f_operator_excl__eq__c2484, &_call_f_operator_excl__eq__c2484); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QDomNodeList &QDomNodeList::operator=(const QDomNodeList &)\n", false, &_init_f_operator_eq__2484, &_call_f_operator_eq__2484); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QDomNodeList::operator==(const QDomNodeList &)\n", true, &_init_f_operator_eq__eq__c2484, &_call_f_operator_eq__eq__c2484); methods += new qt_gsi::GenericMethod ("size", "@brief Method int QDomNodeList::size()\n", true, &_init_f_size_c0, &_call_f_size_c0); return methods; } gsi::Class decl_QDomNodeList ("QtXml", "QDomNodeList", + gsi::method_ext("==", &QDomNodeList_operator_eq, gsi::arg ("other"), "@brief Method bool QDomNodeList::operator==(const QDomNodeList &) const") + + gsi::method_ext("!=", &QDomNodeList_operator_ne, gsi::arg ("other"), "@brief Method bool QDomNodeList::operator!=(const QDomNodeList &) const") ++ methods_QDomNodeList (), "@qt\n@brief Binding of QDomNodeList"); From fa618a5b761cf3267128dac35240641b8968386b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 9 Apr 2025 23:35:09 +0200 Subject: [PATCH 24/76] Fixing the strict weak ordering issue inside the edge processor --- src/db/db/dbEdgeProcessor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index fd3ffb113..c26b5569f 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -1321,7 +1321,7 @@ struct edge_xmin_at_yinterval_double_compare { if (edge_xmax (a) < edge_xmin (b)) { return true; - } else if (edge_xmin (a) >= edge_xmax (b)) { + } else if (edge_xmin (a) > edge_xmax (b)) { return false; } else { C xa = edge_xmin_at_yinterval_double (a, m_y1, m_y2); From d317dc2fe3a97d00f2153528e3e169a9918d898f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 24 Apr 2025 23:30:50 +0200 Subject: [PATCH 25/76] Fixed issue #2025 (brackets get added on PCell parameters) --- src/edt/edt/edtPCellParametersPage.cc | 50 +++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index 27ca1a261..8f27d312b 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -47,6 +47,31 @@ namespace edt { +static std::string variant_list_to_string (const tl::Variant &value) +{ + if (! value.is_list ()) { + tl::Variant v = tl::Variant::empty_list (); + v.push (value); + return v.to_parsable_string (); + } + + for (auto i = value.begin (); i != value.end (); ++i) { + if (! i->is_a_string () || std::string (i->to_string ()).find (",") != std::string::npos) { + return value.to_parsable_string (); + } + } + + // otherwise we can plainly combine the strings with "," + std::string res; + for (auto i = value.begin (); i != value.end (); ++i) { + if (i != value.begin ()) { + res += ","; + } + res += i->to_string (); + } + return res; +} + static void set_value (const db::PCellParameterDeclaration &p, QWidget *widget, const tl::Variant &value) { if (p.get_choices ().empty ()) { @@ -91,7 +116,7 @@ static void set_value (const db::PCellParameterDeclaration &p, QWidget *widget, QLineEdit *le = dynamic_cast (widget); if (le) { le->blockSignals (true); - le->setText (value.to_qstring ()); + le->setText (tl::to_qstring (variant_list_to_string (value))); le->blockSignals (false); } } @@ -905,8 +930,29 @@ PCellParametersPage::get_parameters_internal (db::ParameterStates &states, bool { QLineEdit *le = dynamic_cast (m_widgets [r]); if (le) { - std::vector values = tl::split (tl::to_string (le->text ()), ","); + + std::string s = tl::to_string (le->text ()); + + // try parsing a bracketed expression + tl::Extractor ex (s.c_str ()); + if (*ex.skip () == '(') { + tl::Variant v; + try { + ex.read (v); + ps.set_value (v); + break; + } catch (...) { + // ignore errors + } + } else if (ex.at_end ()) { + ps.set_value (tl::Variant::empty_list ()); + break; + } + + // otherwise: plain splitting at comma + std::vector values = tl::split (s, ","); ps.set_value (tl::Variant (values.begin (), values.end ())); + } } break; From 148498f8405751eca5a23905ca248c8f33b4a6ca Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Apr 2025 16:15:25 +0200 Subject: [PATCH 26/76] Fixing issue #2026 (after 2.5D display main 2D layout does not display anymore with visible shapes) --- .../view_25d/lay_plugin/layD25ViewWidget.cc | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index b3b0a0e31..7a29a7ff0 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -661,15 +661,15 @@ D25ViewWidget::enter (const db::RecursiveShapeIterator *iter, double zstart, dou void D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double zstop) { - // try to establish a default color from the region's origin if required - const db::RecursiveShapeIterator *iter = 0; const db::OriginalLayerRegion *original = dynamic_cast (data.delegate ()); if (original) { - iter = original->iter (); + // try to establish a default color from the region's origin if required + auto it = original->begin_iter (); + enter (&it.first, zstart, zstop); + } else { + enter (0, zstart, zstop); } - enter (iter, zstart, zstop); - tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); render_region (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); } @@ -677,15 +677,15 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double void D25ViewWidget::entry (const db::Edges &data, double dbu, double zstart, double zstop) { - // try to establish a default color from the region's origin if required - const db::RecursiveShapeIterator *iter = 0; const db::OriginalLayerEdges *original = dynamic_cast (data.delegate ()); if (original) { - iter = original->iter (); + // try to establish a default color from the region's origin if required + auto it = original->begin_iter (); + enter (&it.first, zstart, zstop); + } else { + enter (0, zstart, zstop); } - enter (iter, zstart, zstop); - tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); render_edges (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); } @@ -693,15 +693,15 @@ D25ViewWidget::entry (const db::Edges &data, double dbu, double zstart, double z void D25ViewWidget::entry (const db::EdgePairs &data, double dbu, double zstart, double zstop) { - // try to establish a default color from the region's origin if required - const db::RecursiveShapeIterator *iter = 0; const db::OriginalLayerEdgePairs *original = dynamic_cast (data.delegate ()); if (original) { - iter = original->iter (); + // try to establish a default color from the region's origin if required + auto it = original->begin_iter (); + enter (&it.first, zstart, zstop); + } else { + enter (0, zstart, zstop); } - enter (iter, zstart, zstop); - tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); render_edge_pairs (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); } From 2bd82af6feb476ffbb83abe2022d76564dc17655 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Apr 2025 16:52:39 +0200 Subject: [PATCH 27/76] Provide a solution of feature request #2024 - There is a new configuration page entry called "Min spacing" for the grid. The default value is 4. The value specifies the grid min spacing in units of UI font height. - A bugfix is included: the ruler now is drawn after the grid, hence is not hidden by it (specifically in checkerboard pattern mode) - To allow bigger grid spacing, the ruler now is allowed to grow bigger than before. --- src/laybasic/laybasic/laybasicConfig.h | 1 + src/layview/layview/GridNetConfigPage.ui | 336 +++++++++++--------- src/layview/layview/layGridNet.cc | 129 +++++--- src/layview/layview/layGridNet.h | 8 + src/layview/layview/layGridNetConfigPage.cc | 5 + 5 files changed, 271 insertions(+), 208 deletions(-) diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h index 1b33e6455..eb99c9d4b 100644 --- a/src/laybasic/laybasic/laybasicConfig.h +++ b/src/laybasic/laybasic/laybasicConfig.h @@ -43,6 +43,7 @@ static const std::string cfg_grid_grid_color ("grid-grid-color"); static const std::string cfg_grid_style0 ("grid-style0"); static const std::string cfg_grid_style1 ("grid-style1"); static const std::string cfg_grid_style2 ("grid-style2"); +static const std::string cfg_grid_density ("grid-density"); static const std::string cfg_grid_visible ("grid-visible"); static const std::string cfg_grid_micron ("grid-micron"); static const std::string cfg_grid_show_ruler ("grid-show-ruler"); diff --git a/src/layview/layview/GridNetConfigPage.ui b/src/layview/layview/GridNetConfigPage.ui index 34db49143..427129ba0 100644 --- a/src/layview/layview/GridNetConfigPage.ui +++ b/src/layview/layview/GridNetConfigPage.ui @@ -7,7 +7,7 @@ 0 0 483 - 341 + 361 @@ -59,6 +59,23 @@ 6 + + + + Far style + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Show ruler + + + @@ -108,7 +125,122 @@ - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + + + + + + + + + + + Grid + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + + + + + + + Ruler + + + + + + + + + + + + + + Axis + + + + + + + + + + + + + + Close style + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Color (all) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + Color @@ -118,14 +250,17 @@ - - - - Qt::Horizontal + + + + Style + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + @@ -169,30 +304,23 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + + - + Color + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - Qt::Horizontal + + + + Color + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -245,141 +373,37 @@ - - - - Far style - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Close style - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Color (all) - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - - - - - - - Grid - - - - - - - Show Ruler - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Style - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Color - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - - - - - - - Axis - - - - - - - Color - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Ruler - - - - + Qt::Horizontal + + + + 1 + + + + + + + Min. spacing + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + (Font height units) + + + diff --git a/src/layview/layview/layGridNet.cc b/src/layview/layview/layGridNet.cc index 2cc8c85a2..b960579b1 100644 --- a/src/layview/layview/layGridNet.cc +++ b/src/layview/layview/layGridNet.cc @@ -41,6 +41,8 @@ namespace lay // ------------------------------------------------------------ // Helper functions to get and set the configuration +int default_density = 4; + static struct { lay::GridNet::GridStyle style; const char *string; @@ -79,6 +81,20 @@ GridNetStyleConverter::to_string (lay::GridNet::GridStyle style) return ""; } +void +GridNetDensityConverter::from_string (const std::string &value, int &density) +{ + density = default_density; // original default + tl::Extractor ex (value.c_str ()); + ex.try_read (density); +} + +std::string +GridNetDensityConverter::to_string (int density) +{ + return tl::to_string (density); +} + // ------------------------------------------------------------ // Implementation of the GridNetPluginDeclaration @@ -92,6 +108,7 @@ GridNetPluginDeclaration::get_options (std::vector < std::pair (cfg_grid_style0, GridNetStyleConverter ().to_string (lay::GridNet::Invisible))); options.push_back (std::pair (cfg_grid_style1, GridNetStyleConverter ().to_string (lay::GridNet::Dots))); options.push_back (std::pair (cfg_grid_style2, GridNetStyleConverter ().to_string (lay::GridNet::TenthDottedLines))); + options.push_back (std::pair (cfg_grid_density, "")); options.push_back (std::pair (cfg_grid_visible, tl::to_string (true))); options.push_back (std::pair (cfg_grid_show_ruler, tl::to_string (true))); // grid-micron is not configured here since some other entity is supposed to do this. @@ -122,7 +139,8 @@ GridNet::GridNet (LayoutViewBase *view) lay::Plugin (view), mp_view (view), m_visible (false), m_show_ruler (true), m_grid (1.0), - m_style0 (Invisible), m_style1 (Invisible), m_style2 (Invisible) + m_style0 (Invisible), m_style1 (Invisible), m_style2 (Invisible), + m_density (default_density) { // .. nothing yet .. } @@ -175,6 +193,12 @@ GridNet::configure (const std::string &name, const std::string &value) GridNetStyleConverter ().from_string (value, style); need_update = test_and_set (m_style2, style); + } else if (name == cfg_grid_density) { + + int density = 0; + GridNetDensityConverter ().from_string (value, density); + need_update = test_and_set (m_density, density); + } else if (name == cfg_grid_show_ruler) { bool sr = false; @@ -246,13 +270,14 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas) // fw is the basic unit of the ruler geometry int fwr = lay::FixedFont::get_font (bmp_canvas->font_resolution ()).width (); + int threshold = std::min (1000, m_density * fwr); double dgrid = trans.ctrans (m_grid); GridStyle style = m_style1; // compute major grid and switch to secondary style if necessary int s = 0; - while (dgrid < fwr * 4) { + while (dgrid < threshold) { if (s == 0) { dgrid *= 2.0; } else if (s == 1) { @@ -279,56 +304,6 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas) int nx = int (dbworld.width () / grid + eps) + 2; int ny = int (dbworld.height () / grid + eps) + 2; - if (m_show_ruler && dgrid < vp.width () * 0.2) { - - int rh = int (floor (0.5 + fwr * 0.8)); - int xoffset = int (floor (0.5 + fwr * 2.5)); - int yoffset = int (floor (0.5 + fwr * 2.5)); - - painter.fill_rect (db::Point (xoffset, vp.height () - yoffset - rh / 2), - db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset + rh / 2), - ruler_color); - - painter.draw_rect (db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset - rh / 2), - db::Point (xoffset + int (floor (0.5 + 2 * dgrid)), vp.height () - yoffset + rh / 2), - ruler_color); - - painter.draw_text (tl::sprintf ("%g \265m", grid * 2).c_str (), - db::Point (xoffset + int (floor (0.5 + trans.ctrans (2 * grid))), vp.height () - yoffset - rh / 2 - 2), - ruler_color, -1, 1); - - if (mp_view->global_trans ().fp_trans () != db::DFTrans ()) { - - // draw a small "F" indicating any global transformation - db::Point pts[] = { - db::Point (-4, -5), - db::Point (-4, 5), - db::Point (4, 5), - db::Point (4, 3), - db::Point (-2, 3), - db::Point (-2, 1), - db::Point (3, 1), - db::Point (3, -1), - db::Point (-2, -1), - db::Point (-2, -5), - db::Point (-4, -5) - }; - - db::Polygon poly; - poly.assign_hull (&pts[0], &pts[0] + (sizeof (pts) / sizeof (pts[0]))); - poly.transform (db::FTrans (mp_view->global_trans ().fp_trans ())); - - for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); !e.at_end (); ++e) { - db::Point p0 (xoffset + 2 * rh, vp.height () - yoffset - rh * 5); - db::Point p1 = p0 + db::Vector (int (floor (0.5 + (*e).p1 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p1 ().y () * 0.1 * rh * 4))); - db::Point p2 = p0 + db::Vector (int (floor (0.5 + (*e).p2 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p2 ().y () * 0.1 * rh * 4))); - painter.draw_line (p1, p2, ruler_color); - } - - } - - } - // draw grid if (style == Dots || style == TenthDottedLines || style == DottedLines || style == LightDottedLines) { @@ -549,6 +524,56 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas) } + if (m_show_ruler && dgrid < vp.width () * 0.4) { + + int rh = int (floor (0.5 + fwr * 0.8)); + int xoffset = int (floor (0.5 + fwr * 2.5)); + int yoffset = int (floor (0.5 + fwr * 2.5)); + + painter.fill_rect (db::Point (xoffset, vp.height () - yoffset - rh / 2), + db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset + rh / 2), + ruler_color); + + painter.draw_rect (db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset - rh / 2), + db::Point (xoffset + int (floor (0.5 + 2 * dgrid)), vp.height () - yoffset + rh / 2), + ruler_color); + + painter.draw_text (tl::sprintf ("%g \265m", grid * 2).c_str (), + db::Point (xoffset + int (floor (0.5 + trans.ctrans (2 * grid))), vp.height () - yoffset - rh / 2 - 2), + ruler_color, -1, 1); + + if (mp_view->global_trans ().fp_trans () != db::DFTrans ()) { + + // draw a small "F" indicating any global transformation + db::Point pts[] = { + db::Point (-4, -5), + db::Point (-4, 5), + db::Point (4, 5), + db::Point (4, 3), + db::Point (-2, 3), + db::Point (-2, 1), + db::Point (3, 1), + db::Point (3, -1), + db::Point (-2, -1), + db::Point (-2, -5), + db::Point (-4, -5) + }; + + db::Polygon poly; + poly.assign_hull (&pts[0], &pts[0] + (sizeof (pts) / sizeof (pts[0]))); + poly.transform (db::FTrans (mp_view->global_trans ().fp_trans ())); + + for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); !e.at_end (); ++e) { + db::Point p0 (xoffset + 2 * rh, vp.height () - yoffset - rh * 5); + db::Point p1 = p0 + db::Vector (int (floor (0.5 + (*e).p1 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p1 ().y () * 0.1 * rh * 4))); + db::Point p2 = p0 + db::Vector (int (floor (0.5 + (*e).p2 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p2 ().y () * 0.1 * rh * 4))); + painter.draw_line (p1, p2, ruler_color); + } + + } + + } + } } diff --git a/src/layview/layview/layGridNet.h b/src/layview/layview/layGridNet.h index 89b63c3f0..d75dcde37 100644 --- a/src/layview/layview/layGridNet.h +++ b/src/layview/layview/layGridNet.h @@ -87,6 +87,7 @@ private: GridStyle m_style0; GridStyle m_style1; GridStyle m_style2; + int m_density; }; class GridNetStyleConverter @@ -96,6 +97,13 @@ public: std::string to_string (lay::GridNet::GridStyle style); }; +class GridNetDensityConverter +{ +public: + void from_string (const std::string &value, int &density); + std::string to_string (int density); +}; + } #endif diff --git a/src/layview/layview/layGridNetConfigPage.cc b/src/layview/layview/layGridNetConfigPage.cc index 6736f72f1..900a31b53 100644 --- a/src/layview/layview/layGridNetConfigPage.cc +++ b/src/layview/layview/layGridNetConfigPage.cc @@ -94,6 +94,10 @@ GridNetConfigPage::setup (lay::Dispatcher *root) style = lay::GridNet::Invisible; root->config_get (cfg_grid_style2, style, GridNetStyleConverter ()); mp_ui->style2_cbx->setCurrentIndex (int (style)); + + int density = 0; + root->config_get (cfg_grid_density, density, GridNetDensityConverter ()); + mp_ui->grid_density_sb->setValue (density); } void @@ -108,6 +112,7 @@ GridNetConfigPage::commit (lay::Dispatcher *root) root->config_set (cfg_grid_style0, lay::GridNet::GridStyle (mp_ui->style0_cbx->currentIndex ()), GridNetStyleConverter ()); root->config_set (cfg_grid_style1, lay::GridNet::GridStyle (mp_ui->style1_cbx->currentIndex ()), GridNetStyleConverter ()); root->config_set (cfg_grid_style2, lay::GridNet::GridStyle (mp_ui->style2_cbx->currentIndex ()), GridNetStyleConverter ()); + root->config_set (cfg_grid_density, mp_ui->grid_density_sb->value (), GridNetDensityConverter ()); } } // namespace lay From 2435e774f45597e2e3f9a0affed8951ab7c8ba81 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Apr 2025 19:05:40 +0200 Subject: [PATCH 28/76] Preventing an internal error when using report after 'input' --- src/drc/drc/built-in-macros/_drc_engine.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index fa5cf402c..15c6356a4 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -1585,7 +1585,8 @@ module DRC self._context("report") do # finish what we got so far - _finish(false) + view = RBA::LayoutView::current + @def_output && @def_output.finish(false, view) @def_output = nil @def_output = _make_report(description, filename, cellname) From ffa42653feb97acfa4e665bc09a35db08771de9e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Apr 2025 22:04:50 +0200 Subject: [PATCH 29/76] Addressing issue #2011 - "report" can now be late in DRC without internal error Yet, the report will only capture the output layers after the report statement has been called. - Text objects don't create zero-area polygons in deep mode XOR now. --- src/db/db/dbLocalOperationUtils.cc | 8 +++++-- src/db/db/dbPolygon.h | 18 ++++++++++++++++ src/db/db/dbRegionLocalOperations.cc | 22 +++++++++++++++---- src/db/unit_tests/dbPolygonTests.cc | 29 ++++++++++++++++++++++++++ src/drc/unit_tests/drcSimpleTests.cc | 10 +++++++++ testdata/drc/drcSimpleTests_94.drc | 26 +++++++++++++++++++++++ testdata/drc/drcSimpleTests_94.gds | Bin 0 -> 606 bytes testdata/drc/drcSimpleTests_au94.gds | Bin 0 -> 734 bytes testdata/drc/drcSimpleTests_au94d.gds | Bin 0 -> 692 bytes 9 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_94.drc create mode 100644 testdata/drc/drcSimpleTests_94.gds create mode 100644 testdata/drc/drcSimpleTests_au94.gds create mode 100644 testdata/drc/drcSimpleTests_au94d.gds diff --git a/src/db/db/dbLocalOperationUtils.cc b/src/db/db/dbLocalOperationUtils.cc index c338dbe6d..51d98807d 100644 --- a/src/db/db/dbLocalOperationUtils.cc +++ b/src/db/db/dbLocalOperationUtils.cc @@ -39,7 +39,9 @@ PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db void PolygonRefToShapesGenerator::put (const db::Polygon &polygon) { tl::MutexLocker locker (&mp_layout->lock ()); - if (m_prop_id != 0) { + if (polygon.is_empty ()) { + // ignore empty polygons + } else if (m_prop_id != 0) { mp_shapes->insert (db::PolygonRefWithProperties (db::PolygonRef (polygon, mp_layout->shape_repository ()), m_prop_id)); } else { mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); @@ -58,7 +60,9 @@ PolygonSplitter::PolygonSplitter (PolygonSink &sink, double max_area_ratio, size void PolygonSplitter::put (const db::Polygon &poly) { - if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) { + if (poly.is_empty ()) { + // ignore empty polygons + } else if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) { std::vector split_polygons; db::split_polygon (poly, split_polygons); diff --git a/src/db/db/dbPolygon.h b/src/db/db/dbPolygon.h index 134412e8e..b56a36271 100644 --- a/src/db/db/dbPolygon.h +++ b/src/db/db/dbPolygon.h @@ -1771,6 +1771,14 @@ public: return true; } + /** + * @brief Returns a value indicating that the polygon is an empty one + */ + bool is_empty () const + { + return m_ctrs.size () == size_t (1) && m_ctrs[0].size () == 0; + } + /** * @brief Returns the number of points in the polygon */ @@ -1879,6 +1887,7 @@ public: for (typename contour_list_type::iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) { h->transform (db::unit_trans (), true /*compress*/, remove_reflected); } + m_bbox = m_ctrs [0].bbox (); return *this; } @@ -2804,6 +2813,7 @@ public: { // compress the polygon by employing the transform method m_hull.transform (db::unit_trans (), true, remove_reflected); + m_bbox = m_hull.bbox (); return *this; } @@ -3022,6 +3032,14 @@ public: return m_hull.is_halfmanhattan (); } + /** + * @brief Returns a value indicating that the polygon is an empty one + */ + bool is_empty () const + { + return m_hull.size () == 0; + } + /** * @brief The number of holes * diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 48d974563..64849b712 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -1618,18 +1618,34 @@ bool_and_or_not_local_operation::do_compute_local (db::Layout *layou } } + db::polygon_ref_generator pr (layout, result); + db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ()); + for (auto i = interactions.begin (); i != interactions.end (); ++i) { const TR &subject = interactions.subject_shape (i->first); if (others.find (subject) != others.end ()) { + + // shortcut (and: keep, not: drop) + // Note that we still normalize and split the polygon, so we get a uniform + // behavior. if (m_is_and) { - result.insert (subject); + db::Polygon poly; + subject.instantiate (poly); + splitter.put (poly); } + } else if (i->second.empty ()) { + // shortcut (not: keep, and: drop) + // Note that we still normalize and split the polygon, so we get a uniform + // behavior. if (! m_is_and) { - result.insert (subject); + db::Polygon poly; + subject.instantiate (poly); + splitter.put (poly); } + } else { for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { ep.insert (*e, p1); @@ -1649,8 +1665,6 @@ bool_and_or_not_local_operation::do_compute_local (db::Layout *layou } db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); - db::polygon_ref_generator pr (layout, result); - db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ()); db::PolygonGenerator pg (splitter, true, true); ep.set_base_verbosity (50); ep.process (pg, op); diff --git a/src/db/unit_tests/dbPolygonTests.cc b/src/db/unit_tests/dbPolygonTests.cc index c9e277d77..dc518c7bd 100644 --- a/src/db/unit_tests/dbPolygonTests.cc +++ b/src/db/unit_tests/dbPolygonTests.cc @@ -69,6 +69,7 @@ TEST(1) EXPECT_EQ (empty == p, true); EXPECT_EQ (p.is_box (), false); + EXPECT_EQ (p.is_empty (), true); std::vector c1, c2, c3; c1.push_back (db::Point (0, 0)); @@ -76,6 +77,7 @@ TEST(1) c1.push_back (db::Point (100, 1000)); c1.push_back (db::Point (100, 0)); p.assign_hull (c1.begin (), c1.end ()); + EXPECT_EQ (p.is_empty (), false); b = p.box (); EXPECT_EQ (p.holes (), size_t (0)); EXPECT_EQ (p.area (), 1000*100); @@ -1404,3 +1406,30 @@ TEST(28) db::Polygon b (db::Box (-1000000000, -1000000000, 1000000000, 1000000000)); EXPECT_EQ (b.perimeter (), 8000000000.0); } + +TEST(29) +{ + // Degenerated boxes and compress + + db::Polygon b (db::Box (10, 20, 10, 20)); + EXPECT_EQ (b.is_empty (), false); + EXPECT_EQ (b == db::Polygon (), false); + EXPECT_EQ (b.to_string (), "(10,20;10,20;10,20;10,20)"); + EXPECT_EQ (double (b.area ()), 0.0); + + b.compress (true); + + EXPECT_EQ (b.is_empty (), true); + EXPECT_EQ (b == db::Polygon (), true); + + db::SimplePolygon sb (db::Box (10, 20, 10, 20)); + EXPECT_EQ (sb.is_empty (), false); + EXPECT_EQ (sb == db::SimplePolygon (), false); + EXPECT_EQ (sb.to_string (), "(10,20;10,20;10,20;10,20)"); + EXPECT_EQ (double (sb.area ()), 0.0); + + sb.compress (true); + + EXPECT_EQ (sb.is_empty (), true); + EXPECT_EQ (sb == db::SimplePolygon (), true); +} diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index f88392542..c618af869 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1694,6 +1694,16 @@ TEST(93d_withAngle) run_test (_this, "93", true); } +TEST(94_texts_in_region_xor) +{ + run_test (_this, "94", false); +} + +TEST(94d_texts_in_region_xor) +{ + run_test (_this, "94", true); +} + TEST(100_edge_interaction_with_count) { run_test (_this, "100", false); diff --git a/testdata/drc/drcSimpleTests_94.drc b/testdata/drc/drcSimpleTests_94.drc new file mode 100644 index 000000000..5809b64af --- /dev/null +++ b/testdata/drc/drcSimpleTests_94.drc @@ -0,0 +1,26 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +x = l1 ^ l2 + +# we detect point-like polygons here by explicitly +# iterating. The enlargement makes sure, we have something +# to write to the output layout. +boxes = x.data.each.collect do |p| + RBA::Box::new(p.bbox.enlarged(10, 10)) +end +x.data = RBA::Region::new(boxes) + +x.output(10, 0) + diff --git a/testdata/drc/drcSimpleTests_94.gds b/testdata/drc/drcSimpleTests_94.gds new file mode 100644 index 0000000000000000000000000000000000000000..7e3a7cd5206cda09c06762097b7c95bc20b1b6ba GIT binary patch literal 606 zcmZQzV_;&6V31*CVt>iN!XU*U#=yY9gUn{&U}E#}bYfr-VP>^+>@@d2w)}&o%MSeo zv!g;7WLWX&V`B^P4`5(m;b353<7HxCWMJcC0-CNP!2JLJe-#D>rUnSjybUPY#J~Wf zVd6{;3@m~`{XB5}Vj%r&;*1O&3_KuH{!UZ{lq9~Xmg;@F#DJ57bSlHWG2>yXwkwQ}V z0sezUy8J*~=eS#M*POv>j=7n=nc0JZ$ORH5^$8-h(S$)Ju*ZtwK{ zWxC%@cb*>?;sjd;Bi3Sv6|BRZL+v{`SP*=`+PbW9a z_s!`=a{M|Q^buFK_>Ae_-2+h#FuTI z{q@xU{Q{^jm?P7pqFhAR0J}KV{xap!#=XPvy7AclF!zRAueud+h|hG?{C}y@>jg hIiCx3yo`cgLO=UdN!F_j>E|!w(D5qC{8grn@Cy^pZF2ws literal 0 HcmV?d00001 From 5efcf836402770b3b446d6c2d7efb3716951198e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Apr 2025 23:24:26 +0200 Subject: [PATCH 30/76] Bugfix: Deep mode XOR needs to maintain the layout origin of the first argument even if it is empty --- src/db/db/dbDeepRegion.cc | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index a86e2ef4a..aaa6e5fb3 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1054,12 +1054,7 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const { const DeepRegion *other_deep = dynamic_cast (other.delegate ()); - if (empty ()) { - - // Nothing to do - return other.delegate ()->clone (); - - } else if (other.empty ()) { + if (other.empty ()) { // Nothing to do return clone (); @@ -1068,6 +1063,18 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const return AsIfFlatRegion::xor_with (other, property_constraint); + } else if (empty ()) { + + // Nothing to do, but to maintain the normal behavior, we have to map the other + // input to our layout if neccessary + if (&other_deep->deep_layer ().layout () == &deep_layer ().layout ()) { + return other.delegate ()->clone (); + } else { + std::unique_ptr other_deep_mapped (dynamic_cast (clone ())); + other_deep_mapped->deep_layer ().add_from (other_deep->deep_layer ()); + return other_deep_mapped.release (); + } + } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { return new DeepRegion (deep_layer ().derived ()); From 5077b22963f70179aadc31e36a30e8e3faf9800e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 00:30:50 +0200 Subject: [PATCH 31/76] Bugfix: deep, empty layers still need a layout attached --- src/buddies/src/bd/strmxor.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 26cf4e3f9..1a5965e83 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -848,13 +848,17 @@ public: if (m_la >= 0) { ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la); - ri_a.set_for_merged_input (true); + } else { + ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), std::vector ()); } + ri_a.set_for_merged_input (true); if (m_lb >= 0) { ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb); - ri_b.set_for_merged_input (true); + } else { + ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), std::vector ()); } + ri_b.set_for_merged_input (true); db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu)); db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu)); From 07eb49d482e92f71365ca85f649bc3beb08e21fa Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 01:11:38 +0200 Subject: [PATCH 32/76] Initializing MALY plugin --- .../streamers/maly/db_plugin/dbMALY.cc | 123 ++++++++++++ src/plugins/streamers/maly/db_plugin/dbMALY.h | 62 ++++++ .../streamers/maly/db_plugin/dbMALYFormat.h | 162 ++++++++++++++++ .../streamers/maly/db_plugin/dbMALYReader.cc | 142 ++++++++++++++ .../streamers/maly/db_plugin/dbMALYReader.h | 148 ++++++++++++++ .../streamers/maly/db_plugin/db_plugin.pro | 15 ++ .../streamers/maly/db_plugin/gsiDeclDbMALY.cc | 133 +++++++++++++ .../maly/lay_plugin/MALYReaderOptionPage.ui | 183 ++++++++++++++++++ .../maly/lay_plugin/layMALYReaderPlugin.cc | 110 +++++++++++ .../maly/lay_plugin/layMALYReaderPlugin.h | 59 ++++++ .../streamers/maly/lay_plugin/lay_plugin.pro | 22 +++ src/plugins/streamers/maly/maly.pro | 10 + .../streamers/maly/unit_tests/dbMALYReader.cc | 108 +++++++++++ .../streamers/maly/unit_tests/unit_tests.pro | 19 ++ 14 files changed, 1296 insertions(+) create mode 100644 src/plugins/streamers/maly/db_plugin/dbMALY.cc create mode 100644 src/plugins/streamers/maly/db_plugin/dbMALY.h create mode 100644 src/plugins/streamers/maly/db_plugin/dbMALYFormat.h create mode 100644 src/plugins/streamers/maly/db_plugin/dbMALYReader.cc create mode 100644 src/plugins/streamers/maly/db_plugin/dbMALYReader.h create mode 100644 src/plugins/streamers/maly/db_plugin/db_plugin.pro create mode 100644 src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc create mode 100644 src/plugins/streamers/maly/lay_plugin/MALYReaderOptionPage.ui create mode 100644 src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.cc create mode 100644 src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.h create mode 100644 src/plugins/streamers/maly/lay_plugin/lay_plugin.pro create mode 100644 src/plugins/streamers/maly/maly.pro create mode 100644 src/plugins/streamers/maly/unit_tests/dbMALYReader.cc create mode 100644 src/plugins/streamers/maly/unit_tests/unit_tests.pro diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.cc b/src/plugins/streamers/maly/db_plugin/dbMALY.cc new file mode 100644 index 000000000..57d67e416 --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.cc @@ -0,0 +1,123 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbMALY.h" +#include "dbMALYReader.h" +#include "dbStream.h" + +#include "tlClassRegistry.h" + +namespace db +{ + +// --------------------------------------------------------------- +// MALYDiagnostics implementation + +MALYDiagnostics::~MALYDiagnostics () +{ + // .. nothing yet .. +} + +// --------------------------------------------------------------- +// MALY format declaration + +class MALYFormatDeclaration + : public db::StreamFormatDeclaration +{ +public: + MALYFormatDeclaration () + { + // .. nothing yet .. + } + + virtual std::string format_name () const { return "MALY"; } + virtual std::string format_desc () const { return "MALY jobdeck"; } + virtual std::string format_title () const { return "MALY (MALY jobdeck format)"; } + virtual std::string file_format () const { return "MALY jobdeck files (*.maly *.MALY)"; } + + virtual bool detect (tl::InputStream &s) const + { + return false; // @@@ + } + + virtual ReaderBase *create_reader (tl::InputStream &s) const + { + return new db::MALYReader (s); + } + + virtual WriterBase *create_writer () const + { + return 0; + // @@@ return new db::MALYWriter (); + } + + virtual bool can_read () const + { + return true; + } + + virtual bool can_write () const + { + return false; + // @@@ return true; + } + + virtual tl::XMLElementBase *xml_reader_options_element () const + { + return new db::ReaderOptionsXMLElement ("mag", + tl::make_member (&db::MALYReaderOptions::dbu, "dbu") + /* @@@ + tl::make_member (&db::MALYReaderOptions::lambda, "lambda") + + tl::make_member (&db::MALYReaderOptions::layer_map, "layer-map") + + tl::make_member (&db::MALYReaderOptions::create_other_layers, "create-other-layers") + + tl::make_member (&db::MALYReaderOptions::keep_layer_names, "keep-layer-names") + + tl::make_member (&db::MALYReaderOptions::merge, "merge") + + tl::make_element, db::MALYReaderOptions> (&db::MALYReaderOptions::lib_paths, "lib-paths", + tl::make_member::const_iterator, std::vector > (&std::vector::begin, &std::vector::end, &std::vector::push_back, "lib-path") + ) + */ + ); + } + + /* @@@ + virtual tl::XMLElementBase *xml_writer_options_element () const + { + return new db::WriterOptionsXMLElement ("mag", + tl::make_member (&db::MALYWriterOptions::lambda, "lambda") + + tl::make_member (&db::MALYWriterOptions::tech, "tech") + + tl::make_member (&db::MALYWriterOptions::write_timestamp, "write-timestamp") + ); + } + @@@ */ +}; + +// NOTE: Because MALY has such a high degree of syntactic freedom, the detection is somewhat +// fuzzy: do MALY at the very end of the detection chain +static tl::RegisteredClass reader_decl (new MALYFormatDeclaration (), 2300, "MALY"); + +// provide a symbol to force linking against +int force_link_MALY = 0; + +} + + diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h new file mode 100644 index 000000000..acd5f1777 --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -0,0 +1,62 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbMALY +#define HDR_dbMALY + +#include "dbPoint.h" + +#include "tlException.h" +#include "tlInternational.h" +#include "tlString.h" +#include "tlAssert.h" + +#include +#include + +namespace db +{ + +/** + * @brief The diagnostics interface for reporting problems in the reader or writer + */ +class MALYDiagnostics +{ +public: + virtual ~MALYDiagnostics (); + + /** + * @brief Issue an error with positional information + */ + virtual void error (const std::string &txt) = 0; + + /** + * @brief Issue a warning with positional information + */ + virtual void warn (const std::string &txt, int warn_level) = 0; +}; + +} + +#endif + diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYFormat.h b/src/plugins/streamers/maly/db_plugin/dbMALYFormat.h new file mode 100644 index 000000000..e2e47b8cb --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/dbMALYFormat.h @@ -0,0 +1,162 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbMALYFormat +#define HDR_dbMALYFormat + +#include "dbSaveLayoutOptions.h" +#include "dbLoadLayoutOptions.h" +#include "dbPluginCommon.h" + +namespace db +{ + +/** + * @brief Structure that holds the MALY specific options for the reader + * NOTE: this structure is non-public linkage by intention. This way it's instantiated + * in all compile units and the shared object does not need to be linked. + */ +class DB_PLUGIN_PUBLIC MALYReaderOptions + : public FormatSpecificReaderOptions +{ +public: + /** + * @brief The constructor + */ + MALYReaderOptions () + : dbu (0.001), + create_other_layers (true) + { + // .. nothing yet .. + } + + /** + * @brief Specify the database unit to produce + * + * Specify the database unit which the resulting layout will receive. + */ + double dbu; + + /** + * @brief Specifies a layer mapping + * + * If a layer mapping is specified, only the given layers are read. + * Otherwise, all layers are read. + * Setting "create_other_layers" to true will make the reader + * create other layers for all layers not given in the layer map. + * Setting an empty layer map and create_other_layers to true effectively + * enables all layers for reading. + */ + db::LayerMap layer_map; + + /** + * @brief A flag indicating that a new layers shall be created + * + * If this flag is set to true, layers not listed in the layer map a created + * too. + */ + bool create_other_layers; + + /** + * @brief Implementation of FormatSpecificReaderOptions + */ + virtual FormatSpecificReaderOptions *clone () const + { + return new MALYReaderOptions (*this); + } + + /** + * @brief Implementation of FormatSpecificReaderOptions + */ + virtual const std::string &format_name () const + { + static const std::string n ("MALY"); + return n; + } +}; + +#if 0 // @@@ +/** + * @brief Structure that holds the MALY specific options for the Writer + * NOTE: this structure is non-public linkage by intention. This way it's instantiated + * in all compile units and the shared object does not need to be linked. + */ +class DB_PLUGIN_PUBLIC MALYWriterOptions + : public FormatSpecificWriterOptions +{ +public: + /** + * @brief The constructor + */ + MALYWriterOptions () + : lambda (0.0), write_timestamp (true) + { + // .. nothing yet .. + } + + /** + * @brief Specifies the lambda value for writing + * + * The lambda value is the basic scaling parameter. + * If this value is set to 0 or negative, the lambda value stored in the layout + * is used (meta data "lambda"). + */ + double lambda; + + /** + * @brief Specifies the technology value for writing Magic files + * + * If this value is set an empty string, the technology store in the layout's + * "technology" meta data is used. + */ + std::string tech; + + /** + * @brief A value indicating whether the real (true) or fake (false) timestamp is written + * + * A fake, static timestamp is useful for comparing files. + */ + bool write_timestamp; + + /** + * @brief Implementation of FormatSpecificWriterOptions + */ + virtual FormatSpecificWriterOptions *clone () const + { + return new MALYWriterOptions (*this); + } + + /** + * @brief Implementation of FormatSpecificWriterOptions + */ + virtual const std::string &format_name () const + { + static std::string n ("MALY"); + return n; + } +}; +#endif + +} + +#endif + diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc new file mode 100644 index 000000000..1d550ca9e --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -0,0 +1,142 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbMALYReader.h" +#include "dbStream.h" +#include "dbObjectWithProperties.h" +#include "dbArray.h" +#include "dbStatic.h" +#include "dbShapeProcessor.h" +#include "dbTechnology.h" + +#include "tlException.h" +#include "tlString.h" +#include "tlClassRegistry.h" +#include "tlFileUtils.h" +#include "tlUri.h" + +#include +#include + +namespace db +{ + +// --------------------------------------------------------------- +// MALYReader + + +MALYReader::MALYReader (tl::InputStream &s) + : m_stream (s), + m_progress (tl::to_string (tr ("Reading MALY file")), 1000), + m_dbu (0.001) +{ + m_progress.set_format (tl::to_string (tr ("%.0fk lines"))); + m_progress.set_format_unit (1000.0); + m_progress.set_unit (100000.0); +} + +MALYReader::~MALYReader () +{ + // .. nothing yet .. +} + +const LayerMap & +MALYReader::read (db::Layout &layout) +{ + return read (layout, db::LoadLayoutOptions ()); +} + +const LayerMap & +MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) +{ + init (options); + + prepare_layers (layout); + + // @@@ + + finish_layers (layout); + return layer_map_out (); +} + +void +MALYReader::error (const std::string &msg) +{ + throw MALYReaderException (msg, m_stream.line_number (), m_stream.source ()); +} + +void +MALYReader::warn (const std::string &msg, int wl) +{ + if (warn_level () < wl) { + return; + } + + if (first_warning ()) { + tl::warn << tl::sprintf (tl::to_string (tr ("In file %s:")), m_stream.source ()); + } + + int ws = compress_warning (msg); + if (ws < 0) { + tl::warn << msg + << tl::to_string (tr (" (line=")) << m_stream.line_number () + << tl::to_string (tr (", file=")) << m_stream.source () + << ")"; + } else if (ws == 0) { + tl::warn << tl::to_string (tr ("... further warnings of this kind are not shown")); + } +} + +std::string +MALYReader::resolve_path (const std::string &path) +{ + tl::URI path_uri (path); + + if (tl::is_absolute (path_uri.path ())) { + + return path_uri.to_string (); + + } else { + + tl::URI source_uri (m_stream.source ()); + source_uri.set_path (tl::dirname (source_uri.path ())); + return source_uri.resolved (tl::URI (path)).to_string (); + + } +} + +void +MALYReader::do_read (db::Layout &layout, db::cell_index_type cell_index, tl::TextInputStream &stream) +{ + try { + + // @@@ + + } catch (tl::Exception &ex) { + error (ex.msg ()); + } +} + +} + diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h new file mode 100644 index 000000000..f60f7ebb8 --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -0,0 +1,148 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbMALYReader +#define HDR_dbMALYReader + +#include "dbPluginCommon.h" +#include "dbNamedLayerReader.h" +#include "dbLayout.h" +#include "dbMALY.h" +#include "dbMALYFormat.h" +#include "dbStreamLayers.h" +#include "dbPropertiesRepository.h" + +#include "tlException.h" +#include "tlInternational.h" +#include "tlProgress.h" +#include "tlString.h" +#include "tlStream.h" + +#include +#include + +namespace db +{ + +class Technology; + +/** + * @brief Generic base class of MALY reader exceptions + */ +class DB_PLUGIN_PUBLIC MALYReaderException + : public ReaderException +{ +public: + MALYReaderException (const std::string &msg, size_t l, const std::string &file) + : ReaderException (tl::sprintf (tl::to_string (tr ("%s (line=%ld, file=%s)")), msg, l, file)) + { } +}; + +/** + * @brief The MALY format stream reader + */ +class DB_PLUGIN_PUBLIC MALYReader + : public NamedLayerReader, + public MALYDiagnostics +{ +public: + typedef std::vector property_value_list; + + /** + * @brief Construct a stream reader object + * + * @param s The stream delegate from which to read stream data from + */ + MALYReader (tl::InputStream &s); + + /** + * @brief Destructor + */ + ~MALYReader (); + + /** + * @brief The basic read method + * + * This method will read the stream data and translate this to + * insert calls into the layout object. This will not do much + * on the layout object beside inserting the objects. + * A set of options can be specified with the LoadLayoutOptions + * object. + * The returned map will contain all layers, the passed + * ones and the newly created ones. + * + * @param layout The layout object to write to + * @param map The LayerMap object + * @param create true, if new layers should be created + * @return The LayerMap object that tells where which layer was loaded + */ + virtual const LayerMap &read (db::Layout &layout, const LoadLayoutOptions &options); + + /** + * @brief The basic read method (without mapping) + * + * This method will read the stream data and translate this to + * insert calls into the layout object. This will not do much + * on the layout object beside inserting the objects. + * This version will read all input layers and return a map + * which tells which MALY layer has been read into which logical + * layer. + * + * @param layout The layout object to write to + * @return The LayerMap object + */ + virtual const LayerMap &read (db::Layout &layout); + + /** + * @brief Format + */ + virtual const char *format () const { return "MALY"; } + + /** + * @brief Issue an error with positional information + * + * Reimplements MALYDiagnostics + */ + virtual void error (const std::string &txt); + + /** + * @brief Issue a warning with positional information + * + * Reimplements MALYDiagnostics + */ + virtual void warn (const std::string &txt, int wl = 1); + +private: + tl::TextInputStream m_stream; + tl::AbsoluteProgress m_progress; + double m_dbu; + + void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); + std::string resolve_path(const std::string &path); +}; + +} + +#endif + diff --git a/src/plugins/streamers/maly/db_plugin/db_plugin.pro b/src/plugins/streamers/maly/db_plugin/db_plugin.pro new file mode 100644 index 000000000..60b947560 --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/db_plugin.pro @@ -0,0 +1,15 @@ + +TARGET = maly +DESTDIR = $$OUT_PWD/../../../../db_plugins + +include($$PWD/../../../db_plugin.pri) + +HEADERS = \ + dbMALY.h \ + dbMALYReader.h \ + dbMALYFormat.h \ + +SOURCES = \ + dbMALY.cc \ + dbMALYReader.cc \ + gsiDeclDbMALY.cc \ diff --git a/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc b/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc new file mode 100644 index 000000000..c9ebcd37e --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc @@ -0,0 +1,133 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbMALY.h" +#include "dbMALYReader.h" +#include "dbLoadLayoutOptions.h" +#include "dbSaveLayoutOptions.h" +#include "gsiDecl.h" + +namespace gsi +{ + +// --------------------------------------------------------------- +// gsi Implementation of specific methods + +static void set_maly_dbu (db::LoadLayoutOptions *options, double dbu) +{ + options->get_options ().dbu = dbu; +} + +static double get_maly_dbu (const db::LoadLayoutOptions *options) +{ + return options->get_options ().dbu; +} + +static void set_layer_map (db::LoadLayoutOptions *options, const db::LayerMap &lm, bool f) +{ + options->get_options ().layer_map = lm; + options->get_options ().create_other_layers = f; +} + +static void set_layer_map1 (db::LoadLayoutOptions *options, const db::LayerMap &lm) +{ + options->get_options ().layer_map = lm; +} + +static db::LayerMap &get_layer_map (db::LoadLayoutOptions *options) +{ + return options->get_options ().layer_map; +} + +static void select_all_layers (db::LoadLayoutOptions *options) +{ + options->get_options ().layer_map = db::LayerMap (); + options->get_options ().create_other_layers = true; +} + +static bool create_other_layers (const db::LoadLayoutOptions *options) +{ + return options->get_options ().create_other_layers; +} + +static void set_create_other_layers (db::LoadLayoutOptions *options, bool l) +{ + options->get_options ().create_other_layers = l; +} + +// extend lay::LoadLayoutOptions with the MALY options +static +gsi::ClassExt maly_reader_options ( + gsi::method_ext ("maly_set_layer_map", &set_layer_map, gsi::arg ("map"), gsi::arg ("create_other_layers"), + "@brief Sets the layer map\n" + "This sets a layer mapping for the reader. The layer map allows selection and translation of the original layers, for example to assign layer/datatype numbers to the named layers.\n" + "@param map The layer map to set.\n" + "@param create_other_layers The flag indicating whether other layers will be created as well. Set to false to read only the layers in the layer map.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_layer_map=", &set_layer_map1, gsi::arg ("map"), + "@brief Sets the layer map\n" + "This sets a layer mapping for the reader. Unlike \\maly_set_layer_map, the 'create_other_layers' flag is not changed.\n" + "@param map The layer map to set.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_select_all_layers", &select_all_layers, + "@brief Selects all layers and disables the layer map\n" + "\n" + "This disables any layer map and enables reading of all layers.\n" + "New layers will be created when required.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_layer_map", &get_layer_map, + "@brief Gets the layer map\n" + "@return A reference to the layer map\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_create_other_layers?", &create_other_layers, + "@brief Gets a value indicating whether other layers shall be created\n" + "@return True, if other layers will be created.\n" + "This attribute acts together with a layer map (see \\maly_layer_map=). Layers not listed in this map are created as well when " + "\\maly_create_other_layers? is true. Otherwise they are ignored.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_create_other_layers=", &set_create_other_layers, gsi::arg ("create"), + "@brief Specifies whether other layers shall be created\n" + "@param create True, if other layers will be created.\n" + "See \\maly_create_other_layers? for a description of this attribute.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_dbu", &get_maly_dbu, + "@brief Specifies the database unit which the reader uses and produces\n" + "See \\maly_dbu= method for a description of this property.\n" + "\nThis property has been added in version 0.26.2.\n" + ), + "" +); + +} + diff --git a/src/plugins/streamers/maly/lay_plugin/MALYReaderOptionPage.ui b/src/plugins/streamers/maly/lay_plugin/MALYReaderOptionPage.ui new file mode 100644 index 000000000..46ec332d2 --- /dev/null +++ b/src/plugins/streamers/maly/lay_plugin/MALYReaderOptionPage.ui @@ -0,0 +1,183 @@ + + + MALYReaderOptionPage + + + + 0 + 0 + 584 + 530 + + + + Form + + + + + + Input Options + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Micron + + + + + + + Database unit + + + + + + + This is the database unit of the resulting layout. Mask pattern with a different grid are adapted to this database unit through scaling. + + + true + + + + + + + + + + + 1 + 1 + + + + + + + Layer Subset And Layer Mapping + + + false + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + Read all layers (additionally to the ones in the mapping table) + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + + + + + lay::LayerMappingWidget + QFrame +
layLayerMappingWidget.h
+ 1 + + enable_all_layers(bool) + +
+
+ + dbu_le + read_all_cbx + + + + + layer_map + enable_all_layers(bool) + read_all_cbx + setChecked(bool) + + + 122 + 186 + + + 109 + 147 + + + + +
diff --git a/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.cc b/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.cc new file mode 100644 index 000000000..c606f8a92 --- /dev/null +++ b/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.cc @@ -0,0 +1,110 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbMALY.h" +#include "dbMALYReader.h" +#include "dbLoadLayoutOptions.h" +#include "layMALYReaderPlugin.h" +#include "ui_MALYReaderOptionPage.h" +#include "gsiDecl.h" + +#include +#include + +namespace lay +{ + +// --------------------------------------------------------------- +// MALYReaderOptionPage definition and implementation + +MALYReaderOptionPage::MALYReaderOptionPage (QWidget *parent) + : StreamReaderOptionsPage (parent) +{ + mp_ui = new Ui::MALYReaderOptionPage (); + mp_ui->setupUi (this); +} + +MALYReaderOptionPage::~MALYReaderOptionPage () +{ + delete mp_ui; + mp_ui = 0; +} + +void +MALYReaderOptionPage::setup (const db::FormatSpecificReaderOptions *o, const db::Technology * /*tech*/) +{ + static const db::MALYReaderOptions default_options; + const db::MALYReaderOptions *options = dynamic_cast (o); + if (!options) { + options = &default_options; + } + + mp_ui->dbu_le->setText (tl::to_qstring (tl::to_string (options->dbu))); + mp_ui->layer_map->set_layer_map (options->layer_map); + mp_ui->read_all_cbx->setChecked (options->create_other_layers); +} + +void +MALYReaderOptionPage::commit (db::FormatSpecificReaderOptions *o, const db::Technology * /*tech*/) +{ + db::MALYReaderOptions *options = dynamic_cast (o); + if (options) { + + tl::from_string_ext (tl::to_string (mp_ui->dbu_le->text ()), options->dbu); + if (options->dbu > 1000.0 || options->dbu < 1e-9) { + throw tl::Exception (tl::to_string (QObject::tr ("Invalid value for database unit"))); + } + + options->layer_map = mp_ui->layer_map->get_layer_map (); + options->create_other_layers = mp_ui->read_all_cbx->isChecked (); + + } +} + +// --------------------------------------------------------------- +// MALYReaderPluginDeclaration definition and implementation + +class MALYReaderPluginDeclaration + : public StreamReaderPluginDeclaration +{ +public: + MALYReaderPluginDeclaration () + : StreamReaderPluginDeclaration (db::MALYReaderOptions ().format_name ()) + { + // .. nothing yet .. + } + + StreamReaderOptionsPage *format_specific_options_page (QWidget *parent) const + { + return new MALYReaderOptionPage (parent); + } + + db::FormatSpecificReaderOptions *create_specific_options () const + { + return new db::MALYReaderOptions (); + } +}; + +static tl::RegisteredClass plugin_decl (new lay::MALYReaderPluginDeclaration (), 10000, "MALYReader"); + +} + diff --git a/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.h b/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.h new file mode 100644 index 000000000..9de96c41a --- /dev/null +++ b/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.h @@ -0,0 +1,59 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_layMALYReaderPlugin_h +#define HDR_layMALYReaderPlugin_h + +#include "layStream.h" +#include + +namespace Ui +{ + class MALYReaderOptionPage; +} + +namespace lay +{ + +class MALYReaderOptionPage + : public StreamReaderOptionsPage +{ +Q_OBJECT + +public: + MALYReaderOptionPage (QWidget *parent); + ~MALYReaderOptionPage (); + + void setup (const db::FormatSpecificReaderOptions *options, const db::Technology *tech); + void commit (db::FormatSpecificReaderOptions *options, const db::Technology *tech); + +private: + Ui::MALYReaderOptionPage *mp_ui; +}; + +} + +#endif + + diff --git a/src/plugins/streamers/maly/lay_plugin/lay_plugin.pro b/src/plugins/streamers/maly/lay_plugin/lay_plugin.pro new file mode 100644 index 000000000..0677a2552 --- /dev/null +++ b/src/plugins/streamers/maly/lay_plugin/lay_plugin.pro @@ -0,0 +1,22 @@ + +TARGET = maly_ui +DESTDIR = $$OUT_PWD/../../../../lay_plugins + +include($$PWD/../../../lay_plugin.pri) + +INCLUDEPATH += $$PWD/../db_plugin +DEPENDPATH += $$PWD/../db_plugin +LIBS += -L$$DESTDIR/../db_plugins -lmaly + +!isEmpty(RPATH) { + QMAKE_RPATHDIR += $$RPATH/db_plugins +} + +HEADERS = \ + layMALYReaderPlugin.h \ + +SOURCES = \ + layMALYReaderPlugin.cc \ + +FORMS = \ + MALYReaderOptionPage.ui \ diff --git a/src/plugins/streamers/maly/maly.pro b/src/plugins/streamers/maly/maly.pro new file mode 100644 index 000000000..0a2501ae9 --- /dev/null +++ b/src/plugins/streamers/maly/maly.pro @@ -0,0 +1,10 @@ + +TEMPLATE = subdirs + +SUBDIRS = db_plugin unit_tests +unit_tests.depends += db_plugin + +!equals(HAVE_QT, "0") { + SUBDIRS += lay_plugin + lay_plugin.depends += db_plugin +} diff --git a/src/plugins/streamers/maly/unit_tests/dbMALYReader.cc b/src/plugins/streamers/maly/unit_tests/dbMALYReader.cc new file mode 100644 index 000000000..d43dbe0cc --- /dev/null +++ b/src/plugins/streamers/maly/unit_tests/dbMALYReader.cc @@ -0,0 +1,108 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbMALYReader.h" +#include "dbLayoutDiff.h" +#include "dbWriter.h" +#include "tlUnitTest.h" + +#include + +static void run_test (tl::TestBase *_this, const std::string &base, const char *file, const char *file_au, const char *map = 0, double lambda = 0.1, double dbu = 0.001, const std::vector *lib_paths = 0) +{ + db::MALYReaderOptions *opt = new db::MALYReaderOptions(); + opt->dbu = dbu; + + db::LayerMap lm; + if (map) { + unsigned int ln = 0; + tl::Extractor ex (map); + while (! ex.at_end ()) { + std::string n; + int l; + ex.read_word_or_quoted (n); + ex.test (":"); + ex.read (l); + ex.test (","); + lm.map (n, ln++, db::LayerProperties (l, 0)); + } + opt->layer_map = lm; + opt->create_other_layers = true; + } + + db::LoadLayoutOptions options; + options.set_options (opt); + + db::Manager m (false); + db::Layout layout (&m), layout2 (&m), layout2_mag (&m), layout_au (&m); + + { + std::string fn (base); + fn += "/maly/"; + fn += file; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (layout, options); + } + + std::string tc_name = layout.cell_name (*layout.begin_top_down ()); + + // normalize the layout by writing to OASIS and reading from .. + + std::string tmp_oas_file = _this->tmp_file (tl::sprintf ("%s.oas", tc_name)); + std::string tmp_maly_file = _this->tmp_file (tl::sprintf ("%s.mag", tc_name)); + + { + tl::OutputStream stream (tmp_oas_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + db::Writer writer (options); + writer.write (layout, stream); + } + + { + tl::InputStream stream (tmp_oas_file); + db::Reader reader (stream); + reader.read (layout2); + } + + { + std::string fn (base); + fn += "/maly/"; + fn += file_au; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (layout_au); + } + + bool equal = db::compare_layouts (layout2, layout_au, db::layout_diff::f_boxes_as_polygons | db::layout_diff::f_verbose | db::layout_diff::f_flatten_array_insts, 1); + if (! equal) { + _this->raise (tl::sprintf ("Compare failed after reading - see %s vs %s\n", tmp_oas_file, file_au)); + } +} + +TEST(1) +{ + run_test (_this, tl::testdata (), "MALY_TEST.maly", "mag_test_au.oas"); +} + diff --git a/src/plugins/streamers/maly/unit_tests/unit_tests.pro b/src/plugins/streamers/maly/unit_tests/unit_tests.pro new file mode 100644 index 000000000..7a6319ee6 --- /dev/null +++ b/src/plugins/streamers/maly/unit_tests/unit_tests.pro @@ -0,0 +1,19 @@ + +DESTDIR_UT = $$OUT_PWD/../../../.. + +TARGET = maly_tests + +include($$PWD/../../../../lib_ut.pri) + +SOURCES = \ + dbMALYReader.cc \ + +INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common +DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common + +LIBS += -L$$DESTDIR_UT -lklayout_db -lklayout_tl -lklayout_gsi + +PLUGINPATH = $$OUT_PWD/../../../../db_plugins +QMAKE_RPATHDIR += $$PLUGINPATH + +LIBS += -L$$PLUGINPATH -lmaly From c55a0757c17444792243ba5d84f185e4c9ea7777 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 14:27:04 +0200 Subject: [PATCH 33/76] Preparations for 0.30.1 --- Changelog | 17 +++++++++++++++++ Changelog.Debian | 7 +++++++ version.sh | 4 ++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index c4450b33d..387183e50 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,20 @@ +0.30.1 (2025-04-27): +* Bugfix: %GITHUB%/issues/2011 Some DRC bugs fixed +* Bug: %GITHUB%/issues/2014 Bug fixes in LEF/DEF reader +* Enhancement: %GITHUB%/issues/2016 Lazy evaluation of PCell also when changing guiding shape properties +* Enhancement: %GITHUB%/issues/2019 Support for Qt 6.9 +* Bugfix: %GITHUB%/issues/2020 String weak ordering issue fixed in edge processor +* Enhancement: %GITHUB%/issues/2024 Option to configure grid density +* Bugfix: %GITHUB%/issues/2025 Brackets get added in List type PCell parameter edit field +* Bugfix: %GITHUB%/issues/2026 Display is dead after opening 2.5d view +* Bugfix/Enhancement: some updates of "strmxor" tool + - strmxor was giving wrong results if cell variants are + present where one variant is covered entirely by a large shape + - parallelization now happens on a per-layer basis (same as for + XOR tool in KLayout) + - Shape count was not consistent in deep mode + - All buddy tools print total runtime with -d11 + 0.30.0 (2025-03-25): * Bug: %GITHUB%/issues/1996 More robust triangulation * Bug: %GITHUB%/issues/2002 Path to polygon conversion issue diff --git a/Changelog.Debian b/Changelog.Debian index 1d2c2b3c3..a69eb136e 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.30.1-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Sun, 27 Apr 2025 14:26:50 +0200 + klayout (0.30.0-1) unstable; urgency=low * New features and bugfixes diff --git a/version.sh b/version.sh index 737a9f907..3b9bf8033 100644 --- a/version.sh +++ b/version.sh @@ -2,10 +2,10 @@ # This script is sourced to define the main version parameters # The main version -KLAYOUT_VERSION="0.30.0" +KLAYOUT_VERSION="0.30.1" # The version used for PyPI (don't use variables here!) -KLAYOUT_PYPI_VERSION="0.30.0" +KLAYOUT_PYPI_VERSION="0.30.1" # The build date KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d") From 9aa8d79bfc56334ca8e3bed648a23bc25d2aa06c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 14:43:53 +0200 Subject: [PATCH 34/76] Enhanced unit test for better debugging --- src/tl/unit_tests/tlFileUtilsTests.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tl/unit_tests/tlFileUtilsTests.cc b/src/tl/unit_tests/tlFileUtilsTests.cc index 2eafe8bc3..2c5e210cc 100644 --- a/src/tl/unit_tests/tlFileUtilsTests.cc +++ b/src/tl/unit_tests/tlFileUtilsTests.cc @@ -1053,7 +1053,7 @@ TEST (25) std::sort (res.begin (), res.end ()); std::sort (au.begin (), au.end ()); - EXPECT_EQ (res == au, true); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*.txt")); au.clear (); @@ -1066,7 +1066,7 @@ TEST (25) std::sort (res.begin (), res.end ()); std::sort (au.begin (), au.end ()); - EXPECT_EQ (res == au, true); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*2.txt")); au.clear (); @@ -1075,7 +1075,7 @@ TEST (25) std::sort (res.begin (), res.end ()); std::sort (au.begin (), au.end ()); - EXPECT_EQ (res == au, true); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); res = tl::glob_expand (tl::combine_path (tl::combine_path (tl::combine_path (p, "**"), "a"), "*2.txt")); au.clear (); @@ -1083,6 +1083,6 @@ TEST (25) std::sort (res.begin (), res.end ()); std::sort (au.begin (), au.end ()); - EXPECT_EQ (res == au, true); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); } From 43310e7f49d3100599055f6cfd2c5b65f3c2670c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 16:07:01 +0200 Subject: [PATCH 35/76] Updating Python stubs --- src/pymod/distutils_src/klayout/dbcore.pyi | 81 +++++++++++---------- src/pymod/distutils_src/klayout/rdbcore.pyi | 8 ++ src/pymod/distutils_src/klayout/tlcore.pyi | 4 +- 3 files changed, 54 insertions(+), 39 deletions(-) diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index a9d4ed50c..65fc5acd7 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -11620,7 +11620,7 @@ class DEdgePair: ... ... -class DEdgePairWithProperties(EdgePair): +class DEdgePairWithProperties(DEdgePair): r""" @brief A DEdgePair object with properties attached. This class represents a combination of a DEdgePair object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. @@ -11685,7 +11685,7 @@ class DEdgePairWithProperties(EdgePair): @brief Returns a string representing the polygon """ ... - def _assign(self, other: EdgePair) -> None: + def _assign(self, other: DEdgePair) -> None: r""" @brief Assigns another object to self """ @@ -15609,8 +15609,7 @@ class DText: Setter: @brief Sets the horizontal alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ size: float r""" @@ -15646,8 +15645,7 @@ class DText: Setter: @brief Sets the vertical alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ x: float r""" @@ -25332,13 +25330,15 @@ class EdgePairs(ShapeCollection): @brief Creates a copy of self """ ... - def __getitem__(self, n: int) -> EdgePair: + def __getitem__(self, n: int) -> Any: r""" @brief Returns the nth edge pair This method returns nil if the index is out of range. It is available for flat edge pairs only - i.e. those for which \has_valid_edge_pairs? is true. Use \flatten to explicitly flatten an edge pair collection. The \each iterator is the more general approach to access the edge pairs. + + Since version 0.30.1, this method returns a \EdgePairWithProperties object. """ ... def __iadd__(self, other: EdgePairs) -> EdgePairs: @@ -29095,7 +29095,7 @@ class Edges(ShapeCollection): @brief Creates a copy of self """ ... - def __getitem__(self, n: int) -> Edge: + def __getitem__(self, n: int) -> Any: r""" @brief Returns the nth edge of the collection @@ -29103,6 +29103,8 @@ class Edges(ShapeCollection): This method returns the raw edge (not merged edges, even if merged semantics is enabled). The \each iterator is the more general approach to access the edges. + + Since version 0.30.1, this method returns an \EdgeWithProperties object. """ ... def __iadd__(self, other: Edges) -> Edges: @@ -34907,11 +34909,11 @@ class Instance: Starting with version 0.25 the displacement is of vector type. Setter: - @brief Sets the displacement vector for the 'a' axis in micrometer units + @brief Sets the displacement vector for the 'a' axis - Like \a= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. + If the instance was not an array instance before it is made one. - This method has been introduced in version 0.25. + This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type. """ b: Vector r""" @@ -34956,10 +34958,10 @@ class Instance: Getter: @brief Gets the basic \CellInstArray object associated with this instance reference. Setter: - @brief Changes the \CellInstArray object to the given one. - This method replaces the instance by the given CellInstArray object. + @brief Returns the basic cell instance array object by giving a micrometer unit object. + This method replaces the instance by the given CellInstArray object and it internally transformed into database units. - This method has been introduced in version 0.22 + This method has been introduced in version 0.25 """ cplx_trans: ICplxTrans r""" @@ -35455,7 +35457,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This method has been introduced in version 0.22. + This const version of the method has been introduced in version 0.25. """ ... @overload @@ -35463,7 +35465,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This const version of the method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ ... def pcell_declaration(self) -> PCellDeclaration_Native: @@ -47706,17 +47708,17 @@ class Netlist: @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index (const version). + @brief Gets the circuit object for a given cell index. If the cell index is not valid or no circuit is registered with this index, nil is returned. - - This constness variant has been introduced in version 0.26.8. """ ... @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index. + @brief Gets the circuit object for a given cell index (const version). If the cell index is not valid or no circuit is registered with this index, nil is returned. + + This constness variant has been introduced in version 0.26.8. """ ... @overload @@ -47738,20 +47740,20 @@ class Netlist: @overload def circuits_by_name(self, name_pattern: str) -> List[Circuit]: r""" - @brief Gets the circuit objects for a given name filter (const version). + @brief Gets the circuit objects for a given name filter. The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. - - This constness variant has been introduced in version 0.26.8. + This method has been introduced in version 0.26.4. """ ... @overload def circuits_by_name(self, name_pattern: str) -> List[Circuit]: r""" - @brief Gets the circuit objects for a given name filter. + @brief Gets the circuit objects for a given name filter (const version). The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. - This method has been introduced in version 0.26.4. + + This constness variant has been introduced in version 0.26.8. """ ... def combine_devices(self) -> None: @@ -47996,7 +47998,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit. + @brief Gets the top circuit (const version). This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -48005,7 +48007,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit (const version). + @brief Gets the top circuit. This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -58384,7 +58386,7 @@ class Region(ShapeCollection): @brief Creates a copy of self """ ... - def __getitem__(self, n: int) -> Polygon: + def __getitem__(self, n: int) -> Any: r""" @brief Returns the nth polygon of the region @@ -58392,6 +58394,8 @@ class Region(ShapeCollection): This method returns the raw polygon (not merged polygons, even if merged semantics is enabled). The \each iterator is the more general approach to access the polygons. + + Since version 0.30.1, this method returns a \PolygonWithProperties object. """ ... def __iadd__(self, other: Region) -> Region: @@ -63169,11 +63173,10 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent an edge. Setter: - @brief Replaces the shape by the given edge - This method replaces the shape by the given edge. This method can only be called for editable layouts. It does not change the user properties of the shape. - Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. + @brief Replaces the shape by the given edge (in micrometer units) + This method replaces the shape by the given edge, like \edge= with a \Edge argument does. This version translates the edge from micrometer units to database units internally. - This method has been introduced in version 0.22. + This method has been introduced in version 0.25. """ edge_pair: Any r""" @@ -63377,10 +63380,11 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent a geometrical primitive that can be converted to a simple polygon. Setter: - @brief Replaces the shape by the given simple polygon (in micrometer units) - This method replaces the shape by the given text, like \simple_polygon= with a \SimplePolygon argument does. This version translates the polygon from micrometer units to database units internally. + @brief Replaces the shape by the given simple polygon object + This method replaces the shape by the given simple polygon object. This method can only be called for editable layouts. It does not change the user properties of the shape. + Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. - This method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ text: Any r""" @@ -68247,8 +68251,7 @@ class Text: Setter: @brief Sets the horizontal alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ size: int r""" @@ -70054,13 +70057,15 @@ class Texts(ShapeCollection): @brief Creates a copy of self """ ... - def __getitem__(self, n: int) -> Text: + def __getitem__(self, n: int) -> Any: r""" @brief Returns the nth text This method returns nil if the index is out of range. It is available for flat texts only - i.e. those for which \has_valid_texts? is true. Use \flatten to explicitly flatten an text collection. The \each iterator is the more general approach to access the texts. + + Since version 0.30.1, this method returns a \TextWithProperties object. """ ... def __iadd__(self, other: Texts) -> Texts: diff --git a/src/pymod/distutils_src/klayout/rdbcore.pyi b/src/pymod/distutils_src/klayout/rdbcore.pyi index 237cf2d15..73e6d9688 100644 --- a/src/pymod/distutils_src/klayout/rdbcore.pyi +++ b/src/pymod/distutils_src/klayout/rdbcore.pyi @@ -655,6 +655,14 @@ class RdbItem: """ ... @overload + def add_value(self, value: db.DText) -> None: + r""" + @brief Adds a text object to the values of this item + @param value The text to add. + This method has been introduced in version 0.30.1 to support text objects with properties. + """ + ... + @overload def add_value(self, value: float) -> None: r""" @brief Adds a numeric value to the values of this item diff --git a/src/pymod/distutils_src/klayout/tlcore.pyi b/src/pymod/distutils_src/klayout/tlcore.pyi index 09483e06c..fd53c5008 100644 --- a/src/pymod/distutils_src/klayout/tlcore.pyi +++ b/src/pymod/distutils_src/klayout/tlcore.pyi @@ -2908,7 +2908,9 @@ class Timer: r""" @brief Gets the current memory usage of the process in Bytes - This method has been introduced in version 0.27. + The returned value is the resident memory size on Linux and MacOS and the working set size on Windows. + + This method has been introduced in version 0.27. The value has been changed to be resident size (instead of virtual size) on Linux in version 0.30. """ ... @classmethod From f1b35d08261b3bbc9c954d8a099be8ebe55bc462 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 16:56:46 +0200 Subject: [PATCH 36/76] WIP (MALY reader) --- src/plugins/streamers/maly/db_plugin/dbMALY.h | 201 ++++++++++++++++++ .../streamers/maly/db_plugin/gsiDeclDbMALY.cc | 27 ++- 2 files changed, 221 insertions(+), 7 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h index acd5f1777..c4f81e8f4 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -25,6 +25,8 @@ #define HDR_dbMALY #include "dbPoint.h" +#include "dbTrans.h" +#include "dbBox.h" #include "tlException.h" #include "tlInternational.h" @@ -56,6 +58,205 @@ public: virtual void warn (const std::string &txt, int warn_level) = 0; }; +/** + * @brief A class representing a title field on a mask + */ +class MALYTitle +{ +public: + /** + * @brief Default constructor + */ + MALYTitle () + : width (0.0), height (0.0), pitch (0.0), type (String), font (Standard) + { } + + /** + * @brief The type of the title + */ + enum Type + { + String = 0, // A user-defined string + Date = 1, // The date + Serial = 2 // A serial number + }; + + /** + * @brief The font to be used + */ + enum Font + { + Standard = 0, // Standard font + Native = 1 // Native tool font + }; + + /** + * @brief The string for "String" type + */ + std::string string; + + /** + * @brief The transformation of the title + * + * The origin of the title is supposed to be in the center of + * the title field. + */ + db::DTrans transformation; + + /** + * @brief Optional font parameters: character width + */ + double width; + + /** + * @brief Optional font parameters: character height + */ + double height; + + /** + * @brief Optional font parameters: character pitch + */ + double pitch; + + /** + * @brief The type of the title + */ + Type type; + + /** + * @brief The font to be used + */ + Font font; +}; + +/** + * @brief A class representing a structure (pattern) on a mask + */ +class MALYStructure +{ + /** + * @brief Default constructor + */ + MALYStructure () + : nx (1), ny (1), dx (0.0), dy (0.0), layer (-1) + { } + + /** + * @brief The (expanded) path of the pattern file + */ + std::string path; + + /** + * @brief The name of the top cell + * If empty, the topcell is determined automatically + */ + std::string topcell; + + /** + * @brief The pattern window in the original file + */ + db::DBox size; + + /** + * @brief The transformation needed to place the original file + */ + db::DCplxTrans transformation; + + /** + * @brief The number of placements in x direction + */ + int nx; + + /** + * @brief The number of placements in y direction + */ + int ny; + + /** + * @brief The placement pitch in x direction (if nx > 1) + */ + double dx; + + /** + * @brief The placement pitch in y direction (if ny > 1) + */ + double dy; + + /** + * @brief The design name + */ + std::string dname; + + /** + * @brief The name for the mask process + */ + std::string mname; + + /** + * @brief The name for the mask tool + */ + std::string ename; + + /** + * @brief The layer used from the OASIS file + * + * A value of -1 means "all". + */ + int layer; +}; + +/** + * @brief A class representing one mask + */ +class MALYMask +{ + /** + * @brief Default constructor + */ + MALYMask () + : size_um (0.0) + { } + + /** + * @brief Size of the mask in micrometers + */ + double size_um; + + /** + * @brief Name of the mask + * + * This is also the name of the layer generated + */ + std::string name; + + /** + * @brief The list of structures + */ + std::list structures; + + /** + * @brief The list of titles + */ + std::list titles; +}; + +/** + * @brief A class representing the MALY file + */ +class MALYData +{ + /** + * @brief Default constructor + */ + MALYData () + { } + + /** + * @brief The masks defined by the file + */ + std::list masks; +}; + } #endif diff --git a/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc b/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc index c9ebcd37e..fa2a9cdcb 100644 --- a/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc +++ b/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc @@ -83,14 +83,18 @@ gsi::ClassExt maly_reader_options ( "@param map The layer map to set.\n" "@param create_other_layers The flag indicating whether other layers will be created as well. Set to false to read only the layers in the layer map.\n" "\n" - "This method has been added in version 0.26.2." + "Layer maps can also be used to map the named MALY mask layers to GDS layer/datatypes.\n" + "\n" + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_layer_map=", &set_layer_map1, gsi::arg ("map"), "@brief Sets the layer map\n" "This sets a layer mapping for the reader. Unlike \\maly_set_layer_map, the 'create_other_layers' flag is not changed.\n" "@param map The layer map to set.\n" "\n" - "This method has been added in version 0.26.2." + "Layer maps can also be used to map the named MALY mask layers to GDS layer/datatypes.\n" + "\n" + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_select_all_layers", &select_all_layers, "@brief Selects all layers and disables the layer map\n" @@ -98,13 +102,13 @@ gsi::ClassExt maly_reader_options ( "This disables any layer map and enables reading of all layers.\n" "New layers will be created when required.\n" "\n" - "This method has been added in version 0.26.2." + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_layer_map", &get_layer_map, "@brief Gets the layer map\n" "@return A reference to the layer map\n" "\n" - "This method has been added in version 0.26.2." + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_create_other_layers?", &create_other_layers, "@brief Gets a value indicating whether other layers shall be created\n" @@ -112,19 +116,28 @@ gsi::ClassExt maly_reader_options ( "This attribute acts together with a layer map (see \\maly_layer_map=). Layers not listed in this map are created as well when " "\\maly_create_other_layers? is true. Otherwise they are ignored.\n" "\n" - "This method has been added in version 0.26.2." + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_create_other_layers=", &set_create_other_layers, gsi::arg ("create"), "@brief Specifies whether other layers shall be created\n" "@param create True, if other layers will be created.\n" "See \\maly_create_other_layers? for a description of this attribute.\n" "\n" - "This method has been added in version 0.26.2." + "This method has been added in version 0.30.2." + ) + + gsi::method_ext ("maly_dbu=", &set_maly_dbu, gsi::arg ("dbu"), + "@brief Specifies the database unit which the reader uses and produces\n" + "The database unit is the final resolution of the produced layout. This physical resolution is usually " + "defined by the layout system - GDS for example typically uses 1nm (maly_dbu=0.001).\n" + "All geometry in the MALY pattern files is brought to the database unit by scaling.\n" + "\n" + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_dbu", &get_maly_dbu, "@brief Specifies the database unit which the reader uses and produces\n" "See \\maly_dbu= method for a description of this property.\n" - "\nThis property has been added in version 0.26.2.\n" + "\n" + "This method has been added in version 0.30.2." ), "" ); From 6e7eff95e7380f5d9544504d0fcb68ae109c7d72 Mon Sep 17 00:00:00 2001 From: klayoutmatthias Date: Sun, 27 Apr 2025 19:16:37 +0200 Subject: [PATCH 37/76] Fixed glob feature for Windows. --- src/tl/tl/tlFileUtils.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc index a3b755db0..1735d853f 100644 --- a/src/tl/tl/tlFileUtils.cc +++ b/src/tl/tl/tlFileUtils.cc @@ -458,6 +458,16 @@ static void glob_partial (const std::string &where, std::vector::co ++pfrom; } +#if defined(_WIN32) + if (where.empty ()) { + // On Windows, we cannot iterate the drives + std::string root = *pfrom; + ++pfrom; + glob_partial (root, pfrom, pto, res); + return; + } +#endif + tl::GlobPattern glob (tl::trimmed_part (*pfrom)); ++pfrom; auto entries = dir_entries (where, true, true, true); From e76be5b071256239ef89bcfcb1fd6c9df1711b98 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 20:55:11 +0200 Subject: [PATCH 38/76] WIP --- .../streamers/maly/db_plugin/dbMALY.cc | 28 +--- src/plugins/streamers/maly/db_plugin/dbMALY.h | 3 + .../streamers/maly/db_plugin/dbMALYReader.cc | 139 ++++++++++++++++++ .../streamers/maly/db_plugin/dbMALYReader.h | 12 +- .../{dbMALYReader.cc => dbMALYReaderTests.cc} | 6 +- .../streamers/maly/unit_tests/unit_tests.pro | 2 +- testdata/maly/MALY_TEST.maly | 15 ++ 7 files changed, 176 insertions(+), 29 deletions(-) rename src/plugins/streamers/maly/unit_tests/{dbMALYReader.cc => dbMALYReaderTests.cc} (92%) create mode 100644 testdata/maly/MALY_TEST.maly diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.cc b/src/plugins/streamers/maly/db_plugin/dbMALY.cc index 57d67e416..b53b4b449 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.cc @@ -57,7 +57,8 @@ public: virtual bool detect (tl::InputStream &s) const { - return false; // @@@ + db::MALYReader reader (s); + return reader.test (); } virtual ReaderBase *create_reader (tl::InputStream &s) const @@ -68,7 +69,6 @@ public: virtual WriterBase *create_writer () const { return 0; - // @@@ return new db::MALYWriter (); } virtual bool can_read () const @@ -79,36 +79,16 @@ public: virtual bool can_write () const { return false; - // @@@ return true; } virtual tl::XMLElementBase *xml_reader_options_element () const { return new db::ReaderOptionsXMLElement ("mag", - tl::make_member (&db::MALYReaderOptions::dbu, "dbu") - /* @@@ - tl::make_member (&db::MALYReaderOptions::lambda, "lambda") + + tl::make_member (&db::MALYReaderOptions::dbu, "dbu") + tl::make_member (&db::MALYReaderOptions::layer_map, "layer-map") + - tl::make_member (&db::MALYReaderOptions::create_other_layers, "create-other-layers") + - tl::make_member (&db::MALYReaderOptions::keep_layer_names, "keep-layer-names") + - tl::make_member (&db::MALYReaderOptions::merge, "merge") + - tl::make_element, db::MALYReaderOptions> (&db::MALYReaderOptions::lib_paths, "lib-paths", - tl::make_member::const_iterator, std::vector > (&std::vector::begin, &std::vector::end, &std::vector::push_back, "lib-path") - ) - */ + tl::make_member (&db::MALYReaderOptions::create_other_layers, "create-other-layers") ); } - - /* @@@ - virtual tl::XMLElementBase *xml_writer_options_element () const - { - return new db::WriterOptionsXMLElement ("mag", - tl::make_member (&db::MALYWriterOptions::lambda, "lambda") + - tl::make_member (&db::MALYWriterOptions::tech, "tech") + - tl::make_member (&db::MALYWriterOptions::write_timestamp, "write-timestamp") - ); - } - @@@ */ }; // NOTE: Because MALY has such a high degree of syntactic freedom, the detection is somewhat diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h index c4f81e8f4..ec904e969 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -134,6 +134,7 @@ public: */ class MALYStructure { +public: /** * @brief Default constructor */ @@ -210,6 +211,7 @@ class MALYStructure */ class MALYMask { +public: /** * @brief Default constructor */ @@ -245,6 +247,7 @@ class MALYMask */ class MALYData { +public: /** * @brief Default constructor */ diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 1d550ca9e..2352fe8ed 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -61,6 +61,22 @@ MALYReader::~MALYReader () // .. nothing yet .. } +bool +MALYReader::test () +{ + return true; // @@@ + try { + + std::string rec = read_record (); + + tl::Extractor ex (rec.c_str ()); + return ex.test ("BEGIN") && ex.test ("MALY"); + + } catch (...) { + return false; + } +} + const LayerMap & MALYReader::read (db::Layout &layout) { @@ -74,12 +90,135 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) prepare_layers (layout); + const db::MALYReaderOptions &specific_options = options.get_options (); + m_dbu = specific_options.dbu; + + set_layer_map (specific_options.layer_map); + set_create_layers (specific_options.create_other_layers); + // @@@ set_keep_layer_names (specific_options.keep_layer_names); + set_keep_layer_names (true); + + MALYData data = read_maly_file (); + // @@@ finish_layers (layout); return layer_map_out (); } +std::string +MALYReader::read_record () +{ + while (! m_stream.at_end ()) { + std::string r = read_record_internal (); + tl::Extractor ex (r.c_str ()); + if (ex.test ("+")) { + error (tl::to_string (tr ("'+' character past first column - did you mean to continue a line?"))); + } else if (! ex.at_end ()) { + return r; + } + } + + return std::string (); +} + +std::string +MALYReader::read_record_internal () +{ + std::string rec; + + while (! m_stream.at_end ()) { + + char c = m_stream.get_char (); + + // skip comments + if (c == '/') { + char cc = m_stream.peek_char (); + if (cc == '/') { + while (! m_stream.at_end () && (c = m_stream.get_char ()) != '\n') + ; + if (m_stream.at_end ()) { + break; + } + } else if (cc == '*') { + m_stream.get_char (); // eat leading "*" + while (! m_stream.at_end () && (m_stream.get_char () != '*' || m_stream.peek_char () != '/')) + ; + if (m_stream.at_end ()) { + error (tl::to_string (tr ("/*...*/ comment not closed"))); + } + m_stream.get_char (); // eat trailing "/" + if (m_stream.at_end ()) { + break; + } + c = m_stream.get_char (); + } + } + + if (c == '\n') { + if (m_stream.peek_char () == '+') { + // continuation line + m_stream.get_char (); // eat "+" + if (m_stream.at_end ()) { + break; + } + c = m_stream.get_char (); + } else { + break; + } + } + + if (c == '"' || c == '\'') { + + rec += c; + + // skip quoted string + char quote = c; + while (! m_stream.at_end ()) { + c = m_stream.get_char (); + rec += c; + if (c == quote) { + quote = 0; + break; + } else if (c == '\\') { + if (m_stream.at_end ()) { + error (tl::to_string (tr ("Unexpected end of file inside quotee string"))); + } + c = m_stream.get_char (); + rec += c; + } else if (c == '\n') { + error (tl::to_string (tr ("Line break inside quoted string"))); + } + } + + if (quote) { + error (tl::to_string (tr ("Unexpected end of file inside quotee string"))); + } + + } else { + rec += c; + } + + } + + return rec; +} + +MALYData +MALYReader::read_maly_file () +{ + // @@@ + std::cout << "@@@ BEGIN_MALY" << std::endl; + std::string rec; + while (! (rec = read_record ()).empty ()) { + std::cout << rec << std::endl; + } + std::cout << "@@@ END_MALY" << std::endl; + // @@@ + + return MALYData (); // @@@ +} + void MALYReader::error (const std::string &msg) { diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index f60f7ebb8..981d3419a 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -76,11 +76,18 @@ public: */ MALYReader (tl::InputStream &s); - /** + /** * @brief Destructor */ ~MALYReader (); + /** + * @brief Tests, if the stream is a valid MALY file + * + * This method can be used for the format detection + */ + bool test (); + /** * @brief The basic read method * @@ -140,6 +147,9 @@ private: void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); std::string resolve_path(const std::string &path); + MALYData read_maly_file (); + std::string read_record (); + std::string read_record_internal (); }; } diff --git a/src/plugins/streamers/maly/unit_tests/dbMALYReader.cc b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc similarity index 92% rename from src/plugins/streamers/maly/unit_tests/dbMALYReader.cc rename to src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc index d43dbe0cc..cb3f749c3 100644 --- a/src/plugins/streamers/maly/unit_tests/dbMALYReader.cc +++ b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc @@ -28,7 +28,7 @@ #include -static void run_test (tl::TestBase *_this, const std::string &base, const char *file, const char *file_au, const char *map = 0, double lambda = 0.1, double dbu = 0.001, const std::vector *lib_paths = 0) +static void run_test (tl::TestBase *_this, const std::string &base, const char *file, const char *file_au, const char *map = 0, double dbu = 0.001) { db::MALYReaderOptions *opt = new db::MALYReaderOptions(); opt->dbu = dbu; @@ -54,7 +54,7 @@ static void run_test (tl::TestBase *_this, const std::string &base, const char * options.set_options (opt); db::Manager m (false); - db::Layout layout (&m), layout2 (&m), layout2_mag (&m), layout_au (&m); + db::Layout layout (&m), layout2 (&m), layout_au (&m); { std::string fn (base); @@ -65,12 +65,12 @@ static void run_test (tl::TestBase *_this, const std::string &base, const char * reader.read (layout, options); } + tl_assert (layout.begin_top_down () != layout.end_top_down ()); std::string tc_name = layout.cell_name (*layout.begin_top_down ()); // normalize the layout by writing to OASIS and reading from .. std::string tmp_oas_file = _this->tmp_file (tl::sprintf ("%s.oas", tc_name)); - std::string tmp_maly_file = _this->tmp_file (tl::sprintf ("%s.mag", tc_name)); { tl::OutputStream stream (tmp_oas_file); diff --git a/src/plugins/streamers/maly/unit_tests/unit_tests.pro b/src/plugins/streamers/maly/unit_tests/unit_tests.pro index 7a6319ee6..a9d89c591 100644 --- a/src/plugins/streamers/maly/unit_tests/unit_tests.pro +++ b/src/plugins/streamers/maly/unit_tests/unit_tests.pro @@ -6,7 +6,7 @@ TARGET = maly_tests include($$PWD/../../../../lib_ut.pri) SOURCES = \ - dbMALYReader.cc \ + dbMALYReaderTests.cc INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common diff --git a/testdata/maly/MALY_TEST.maly b/testdata/maly/MALY_TEST.maly new file mode 100644 index 000000000..0fd08fc48 --- /dev/null +++ b/testdata/maly/MALY_TEST.maly @@ -0,0 +1,15 @@ + +// a comment + +BEGIN /* a multiline comment + + +*/ MALY + +// a comment + +SREF " \"// /*hello*/ " SIZE ++ 10,10 + + + From cd468d4d67501dcdc57121a1292f03dc5a3d12ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 23:00:46 +0200 Subject: [PATCH 39/76] WIP --- .../streamers/maly/db_plugin/dbMALYReader.cc | 464 +++++++++++++++++- .../streamers/maly/db_plugin/dbMALYReader.h | 19 +- testdata/maly/MALY_TEST.maly | 52 +- 3 files changed, 502 insertions(+), 33 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 2352fe8ed..aa5e19a20 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -43,8 +43,82 @@ namespace db { // --------------------------------------------------------------- -// MALYReader +// Some helper structures to collect data +struct MALYReaderTitleSpec +{ + MALYReaderTitleSpec () + : enabled (false), width (1.0), height (1.0), pitch (1.0) + { } + + bool enabled; + db::DTrans trans; + double width, height, pitch; +}; + +struct MALYReaderTitleData +{ + MALYReaderTitleData () + { } + + MALYReaderTitleSpec date_spec; + MALYReaderTitleSpec serial_spec; + std::list > string_titles; +}; + +struct MALYReaderParametersData +{ + MALYReaderParametersData () + : base (Center), array_base (Center), masksize (0.0), maskmirror (false), font (MALYTitle::Standard) + { } + + enum Base + { + Origin, + Center, + LowerLeft + }; + + Base base; + Base array_base; + double masksize; + bool maskmirror; + MALYTitle::Font font; + std::list > roots; +}; + +struct MALYReaderStrRefData +{ + MALYReaderStrRefData () + : layer (-1), scale (1.0), nx (1), ny (1), dx (0.0), dy (0.0) + { } + + std::string file; + std::string name; + int layer; + db::DVector org; + db::DBox size; + double scale; + int nx, ny; + double dx, dy; +}; + +struct MALYReaderStrGroupData +{ + std::string name; + std::list refs; +}; + +struct MALYReaderMaskData +{ + MALYReaderParametersData parameters; + MALYReaderTitleData title; + std::list strgroups; +}; + + +// --------------------------------------------------------------- +// MALYReader MALYReader::MALYReader (tl::InputStream &s) : m_stream (s), @@ -64,12 +138,9 @@ MALYReader::~MALYReader () bool MALYReader::test () { - return true; // @@@ try { - std::string rec = read_record (); - - tl::Extractor ex (rec.c_str ()); + tl::Extractor ex = read_record (); return ex.test ("BEGIN") && ex.test ("MALY"); } catch (...) { @@ -106,20 +177,31 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) return layer_map_out (); } -std::string +void +MALYReader::unget_record () +{ + m_record_returned = m_record; +} + +tl::Extractor MALYReader::read_record () { + if (! m_record_returned.empty ()) { + m_record = m_record_returned; + return tl::Extractor (m_record.c_str ()); + } + while (! m_stream.at_end ()) { - std::string r = read_record_internal (); - tl::Extractor ex (r.c_str ()); + m_record = read_record_internal (); + tl::Extractor ex (m_record.c_str ()); if (ex.test ("+")) { error (tl::to_string (tr ("'+' character past first column - did you mean to continue a line?"))); } else if (! ex.at_end ()) { - return r; + return ex; } } - return std::string (); + return tl::Extractor (); } std::string @@ -207,16 +289,362 @@ MALYReader::read_record_internal () MALYData MALYReader::read_maly_file () { - // @@@ - std::cout << "@@@ BEGIN_MALY" << std::endl; - std::string rec; - while (! (rec = read_record ()).empty ()) { - std::cout << rec << std::endl; + MALYData data; + try { + do_read_maly_file (data); + } catch (tl::Exception &ex) { + error (ex.msg ()); } - std::cout << "@@@ END_MALY" << std::endl; - // @@@ + return data; +} - return MALYData (); // @@@ +void +MALYReader::extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec) +{ + double x = 0.0, y = 0.0; + bool ymirror = false; + int rot = 0; + + ex.read (x); + ex.read (y); + + if (ex.test ("SIZE")) { + ex.read (spec.width); + ex.read (spec.height); + ex.read (spec.pitch); + } + + if (ex.test ("MIRROR")) { + if (ex.test ("Y")) { + ymirror = true; + } else if (ex.test ("OFF")) { // @@@ + ymirror = false; + } else { + error (tl::to_string (tr ("Expected 'Y' or 'OFF' for MIRROR spec"))); + } + } + + if (ex.test ("ROTATE")) { + unsigned int a = 0; + ex.read (a); + rot = (a / 90) % 4; + } + + spec.trans = db::DTrans (rot, ymirror, db::DVector (x, y)); +} + +static +MALYReaderParametersData::Base string_to_base (const std::string &string) +{ + if (string == "ORIGIN") { + return MALYReaderParametersData::Origin; + } else if (string == "LOWERLEFT") { // @@@? + return MALYReaderParametersData::LowerLeft; + } else if (string == "CENTER") { + return MALYReaderParametersData::Center; + } else { + // @@@ error + return MALYReaderParametersData::Center; + } +} + +void +MALYReader::read_parameter (MALYReaderParametersData &data) +{ + while (true) { + + tl::Extractor ex = read_record (); + if (ex.test ("MASKMIRROR")) { + + if (ex.test ("NONE")) { + data.maskmirror = false; + } else if (ex.test ("Y")) { + data.maskmirror = true; + } else { + error (tl::to_string (tr ("Expected value Y or NONE for MASKMIRROR"))); + } + + } else if (ex.test ("MASKSIZE")) { + + data.masksize = 0.0; + ex.read (data.masksize); + + } else if (ex.test ("FONT")) { + + if (ex.test ("STANDARD")) { + data.font = MALYTitle::Standard; + } else if (ex.test ("NATIVE")) { + data.font = MALYTitle::Native; + } else { + error (tl::to_string (tr ("Expected value STANDARD or NATIVE for FONT"))); + } + + } else if (ex.test ("BASE")) { + + std::string base; + ex.read_word (base); + data.base = string_to_base (base); + + } else if (ex.test ("ARYBASE")) { + + std::string base; + ex.read_word (base); + data.array_base = string_to_base (base); + + } else if (ex.test ("REFERENCE")) { + + ex.expect ("TOOL"); + + std::string para; + ex.read_word_or_quoted (para); + // @@@ TODO: what to do with "para" + + ex.expect_end (); + + } else if (ex.test ("ROOT")) { + + std::string format, path; + ex.read_word_or_quoted (format); + ex.read_word_or_quoted (path, ".\\/+-"); + ex.expect_end (); + + data.roots.push_back (std::make_pair (format, path)); + + } else if (ex.test ("END")) { + + ex.expect ("PARAMETER"); + return; + + } else { + error (tl::to_string (tr ("Parameter spec expected or END PARAMETER"))); + } + + } +} + +void +MALYReader::read_title (MALYReaderTitleData &data) +{ + while (true) { + + tl::Extractor ex = read_record (); + if (ex.test ("DATE")) { + + if (ex.test ("OFF")) { + data.date_spec.enabled = false; + } else { + data.date_spec.enabled = true; + extract_title_trans (ex, data.date_spec); + ex.expect_end (); + } + + } else if (ex.test ("SERIAL")) { + + if (ex.test ("OFF")) { + data.serial_spec.enabled = false; + } else { + data.serial_spec.enabled = true; + extract_title_trans (ex, data.serial_spec); + ex.expect_end (); + } + + } else if (ex.test ("STRING")) { + + std::string text; + ex.read_word_or_quoted (text); + + data.string_titles.push_back (std::make_pair (text, MALYReaderTitleSpec ())); + data.string_titles.back ().second.enabled = true; + extract_title_trans (ex, data.string_titles.back ().second); + + ex.expect_end (); + + } else if (ex.test ("END")) { + + ex.expect ("TITLE"); + return; + + } else { + error (tl::to_string (tr ("Title spec expected or END TITLE"))); + } + + } +} + +void +MALYReader::read_strgroup (MALYReaderStrGroupData &data) +{ + while (true) { + + bool is_sref = false; + + tl::Extractor ex = read_record (); + if ((is_sref = ex.test ("SREF")) || ex.test ("AREF")) { + + data.refs.push_back (MALYReaderStrRefData ()); + MALYReaderStrRefData &ref = data.refs.back (); + + ex.read_word_or_quoted (ref.file); + ex.read_word_or_quoted (ref.name); + ex.read (ref.layer); + + if (ex.test ("ORG")) { + double x = 0.0, y = 0.0; + ex.read (x); + ex.read (y); + ref.org = db::DVector (x, y); + } + + if (ex.test ("SIZE")) { + double l = 0.0, b = 0.0, r = 0.0, t = 0.0; + ex.read (l); + ex.read (b); + ex.read (r); + ex.read (t); + ref.size = db::DBox (l, b, r, t); + } + + if (ex.test ("SCALE")) { + ex.read (ref.scale); + } + + if (! is_sref && ex.test ("ITERATION")) { + ex.read (ref.nx); + ex.read (ref.ny); + ex.read (ref.dx); + ex.read (ref.dy); + } + + ex.expect_end (); + + } else if (ex.test ("END")) { + + ex.expect ("STRGROUP"); + return; + + } else { + error (tl::to_string (tr ("SREF or AREF spec expected or END STRGROUP"))); + } + + } +} + +void +MALYReader::read_mask (MALYReaderMaskData &mask, bool cmask) +{ + while (true) { + + tl::Extractor ex = read_record (); + if (ex.test ("BEGIN")) { + + if (ex.test ("PARAMETER")) { + + ex.expect_end (); + read_parameter (mask.parameters); + + } else if (ex.test ("TITLE")) { + + ex.expect_end (); + read_title (mask.title); + + } else if (ex.test ("STRGROUP")) { + + mask.strgroups.push_back (MALYReaderStrGroupData ()); + + ex.read_word_or_quoted (mask.strgroups.back ().name); + ex.expect_end (); + + read_strgroup (mask.strgroups.back ()); + + } else { + error (tl::to_string (tr ("Mask component expected (PARAMETER, TITLE, STRGROUP)"))); + } + + } else if (ex.test ("END")) { + + ex.expect (cmask ? "CMASK" : "MASK"); + break; + + } else { + error (tl::to_string (tr ("Mask component expected (PARAMETER, TITLE, STRGROUP)"))); + } + + } +} + +bool +MALYReader::read_maskset (MALYData &data) +{ + tl::Extractor ex = read_record (); + if (! ex.test ("BEGIN") || ! ex.test ("MASKSET")) { + unget_record (); + return false; + } + + MALYReaderMaskData cmask; + std::list masks; + + while (true) { + + ex = read_record (); + + if (ex.test ("END")) { + + ex.expect ("MASKSET"); + ex.expect_end (); + // @@@ create_masks (cmask, masks, data); + return true; + + } else if (ex.test ("BEGIN")) { + + MALYReaderMaskData *mm = 0; + bool cm = false; + if (ex.test ("MASK")) { + masks.push_back (MALYReaderMaskData ()); + mm = &masks.back (); + } else if (ex.test ("CMASK")) { + mm = &cmask; + cm = true; + } else { + error (tl::to_string (tr ("'BEGIN MASK' or 'BEGIN CMASK' record expected"))); + } + + ex.expect_end (); + read_mask (*mm, cm); + + } else { + error (tl::to_string (tr ("'BEGIN MASK' or 'BEGIN CMASK' record expected"))); + } + + } +} + +void +MALYReader::do_read_maly_file (MALYData &data) +{ + tl::Extractor ex = read_record (); + if (! ex.test ("BEGIN") || ! ex.test ("MALY")) { + error (tl::to_string (tr ("Header expected ('BEGIN MALY')"))); + } + + std::string version; + ex.read (version, "."); + // @@@ TODO: what to do with version string? + + ex.expect_end (); + + while (read_maskset (data)) + ; + + ex = read_record (); + if (! ex.test ("END") || ! ex.test ("MALY")) { + error (tl::to_string (tr ("Terminator expected ('END MALY')"))); + } + + ex = read_record (); + if (! ex.at_end ()) { + error (tl::to_string (tr ("Records found past end of file"))); + } } void diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index 981d3419a..0272b6886 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -45,7 +45,11 @@ namespace db { -class Technology; +class MALYReaderMaskData; +class MALYReaderTitleData; +class MALYReaderParametersData; +class MALYReaderStrGroupData; +class MALYReaderTitleSpec; /** * @brief Generic base class of MALY reader exceptions @@ -144,12 +148,23 @@ private: tl::TextInputStream m_stream; tl::AbsoluteProgress m_progress; double m_dbu; + std::string m_record; + std::string m_record_returned; void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); std::string resolve_path(const std::string &path); MALYData read_maly_file (); - std::string read_record (); + tl::Extractor read_record (); + void unget_record (); std::string read_record_internal (); + void do_read_maly_file (MALYData &data); + bool read_maskset (MALYData &data); + void read_mask (MALYReaderMaskData &mask, bool cmask); + void read_title (MALYReaderTitleData &mask); + void read_parameter (MALYReaderParametersData &mask); + void read_strgroup (MALYReaderStrGroupData &mask); + db::DTrans extract_title_trans (tl::Extractor &ex); + void extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec); }; } diff --git a/testdata/maly/MALY_TEST.maly b/testdata/maly/MALY_TEST.maly index 0fd08fc48..ca7b71601 100644 --- a/testdata/maly/MALY_TEST.maly +++ b/testdata/maly/MALY_TEST.maly @@ -1,15 +1,41 @@ -// a comment +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING TEST 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY -BEGIN /* a multiline comment - - -*/ MALY - -// a comment - -SREF " \"// /*hello*/ " SIZE -+ 10,10 - - - From 7820733bd59881ed5ca6e356c839bc52a575bc47 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 29 Apr 2025 00:28:46 +0200 Subject: [PATCH 40/76] WIP --- .../streamers/maly/db_plugin/dbMALYReader.cc | 177 +++++++++++------- .../streamers/maly/db_plugin/dbMALYReader.h | 6 +- 2 files changed, 111 insertions(+), 72 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index aa5e19a20..ebf2d7b0b 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -348,13 +348,71 @@ MALYReaderParametersData::Base string_to_base (const std::string &string) } } +bool +MALYReader::begin_section (tl::Extractor &ex, const std::string &name) +{ + tl::Extractor ex_saved = ex; + + if (ex.test ("BEGIN")) { + if (name.empty ()) { + m_sections.push_back (std::string ()); + ex.read_word (m_sections.back ()); + return true; + } else if (ex.test (name.c_str ())) { + m_sections.push_back (name); + return true; + } + } + + ex = ex_saved; + return false; +} + +bool +MALYReader::end_section (tl::Extractor &ex) +{ + tl_assert (! m_sections.empty ()); + if (ex.at_end ()) { + + error (tl::to_string (tr ("Unexpected end of file during section"))); + return false; + + } else if (ex.test ("END")) { + + ex.expect (m_sections.back ().c_str ()); + m_sections.pop_back (); + return true; + + } else { + + return false; + + } +} + +void +MALYReader::skip_section () +{ + while (true) { + tl::Extractor ex = read_record (); + if (begin_section (ex)) { + skip_section (); + } else if (end_section (ex)) { + break; + } + } +} + void MALYReader::read_parameter (MALYReaderParametersData &data) { while (true) { tl::Extractor ex = read_record (); - if (ex.test ("MASKMIRROR")) { + + if (end_section (ex)) { + break; + } else if (ex.test ("MASKMIRROR")) { if (ex.test ("NONE")) { data.maskmirror = false; @@ -410,13 +468,8 @@ MALYReader::read_parameter (MALYReaderParametersData &data) data.roots.push_back (std::make_pair (format, path)); - } else if (ex.test ("END")) { - - ex.expect ("PARAMETER"); - return; - } else { - error (tl::to_string (tr ("Parameter spec expected or END PARAMETER"))); + warn (tl::to_string (tr ("Unknown record ignored"))); } } @@ -428,7 +481,10 @@ MALYReader::read_title (MALYReaderTitleData &data) while (true) { tl::Extractor ex = read_record (); - if (ex.test ("DATE")) { + + if (end_section (ex)) { + break; + } else if (ex.test ("DATE")) { if (ex.test ("OFF")) { data.date_spec.enabled = false; @@ -459,13 +515,8 @@ MALYReader::read_title (MALYReaderTitleData &data) ex.expect_end (); - } else if (ex.test ("END")) { - - ex.expect ("TITLE"); - return; - } else { - error (tl::to_string (tr ("Title spec expected or END TITLE"))); + warn (tl::to_string (tr ("Unknown record ignored"))); } } @@ -479,7 +530,9 @@ MALYReader::read_strgroup (MALYReaderStrGroupData &data) bool is_sref = false; tl::Extractor ex = read_record (); - if ((is_sref = ex.test ("SREF")) || ex.test ("AREF")) { + if (end_section (ex)) { + break; + } else if ((is_sref = ex.test ("SREF")) || ex.test ("AREF")) { data.refs.push_back (MALYReaderStrRefData ()); MALYReaderStrRefData &ref = data.refs.back (); @@ -517,56 +570,44 @@ MALYReader::read_strgroup (MALYReaderStrGroupData &data) ex.expect_end (); - } else if (ex.test ("END")) { - - ex.expect ("STRGROUP"); - return; - } else { - error (tl::to_string (tr ("SREF or AREF spec expected or END STRGROUP"))); + warn (tl::to_string (tr ("Unknown record ignored"))); } } } void -MALYReader::read_mask (MALYReaderMaskData &mask, bool cmask) +MALYReader::read_mask (MALYReaderMaskData &mask) { while (true) { tl::Extractor ex = read_record (); - if (ex.test ("BEGIN")) { - - if (ex.test ("PARAMETER")) { - - ex.expect_end (); - read_parameter (mask.parameters); - - } else if (ex.test ("TITLE")) { - - ex.expect_end (); - read_title (mask.title); - - } else if (ex.test ("STRGROUP")) { - - mask.strgroups.push_back (MALYReaderStrGroupData ()); - - ex.read_word_or_quoted (mask.strgroups.back ().name); - ex.expect_end (); - - read_strgroup (mask.strgroups.back ()); - - } else { - error (tl::to_string (tr ("Mask component expected (PARAMETER, TITLE, STRGROUP)"))); - } - - } else if (ex.test ("END")) { - - ex.expect (cmask ? "CMASK" : "MASK"); + if (end_section (ex)) { break; + } else if (begin_section (ex, "PARAMETER")) { + ex.expect_end (); + read_parameter (mask.parameters); + + } else if (begin_section (ex, "TITLE")) { + + ex.expect_end (); + read_title (mask.title); + + } else if (begin_section (ex, "STRGROUP")) { + + mask.strgroups.push_back (MALYReaderStrGroupData ()); + + ex.read_word_or_quoted (mask.strgroups.back ().name); + ex.expect_end (); + + read_strgroup (mask.strgroups.back ()); + + } else if (begin_section (ex)) { + skip_section (); } else { - error (tl::to_string (tr ("Mask component expected (PARAMETER, TITLE, STRGROUP)"))); + warn (tl::to_string (tr ("Unknown record ignored"))); } } @@ -576,7 +617,8 @@ bool MALYReader::read_maskset (MALYData &data) { tl::Extractor ex = read_record (); - if (! ex.test ("BEGIN") || ! ex.test ("MASKSET")) { + + if (! begin_section (ex, "MASKSET")) { unget_record (); return false; } @@ -588,32 +630,25 @@ MALYReader::read_maskset (MALYData &data) ex = read_record (); - if (ex.test ("END")) { + if (end_section (ex)) { - ex.expect ("MASKSET"); ex.expect_end (); // @@@ create_masks (cmask, masks, data); return true; - } else if (ex.test ("BEGIN")) { - - MALYReaderMaskData *mm = 0; - bool cm = false; - if (ex.test ("MASK")) { - masks.push_back (MALYReaderMaskData ()); - mm = &masks.back (); - } else if (ex.test ("CMASK")) { - mm = &cmask; - cm = true; - } else { - error (tl::to_string (tr ("'BEGIN MASK' or 'BEGIN CMASK' record expected"))); - } + } else if (begin_section (ex, "MASK")) { ex.expect_end (); - read_mask (*mm, cm); + masks.push_back (MALYReaderMaskData ()); + read_mask (masks.back ()); + + } else if (begin_section (ex, "CMASK")) { + + ex.expect_end (); + read_mask (cmask); } else { - error (tl::to_string (tr ("'BEGIN MASK' or 'BEGIN CMASK' record expected"))); + warn (tl::to_string (tr ("Unknown record ignored"))); } } @@ -623,7 +658,7 @@ void MALYReader::do_read_maly_file (MALYData &data) { tl::Extractor ex = read_record (); - if (! ex.test ("BEGIN") || ! ex.test ("MALY")) { + if (! begin_section (ex, "MALY")) { error (tl::to_string (tr ("Header expected ('BEGIN MALY')"))); } @@ -637,7 +672,7 @@ MALYReader::do_read_maly_file (MALYData &data) ; ex = read_record (); - if (! ex.test ("END") || ! ex.test ("MALY")) { + if (! end_section (ex)) { error (tl::to_string (tr ("Terminator expected ('END MALY')"))); } diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index 0272b6886..e8544782a 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -150,6 +150,7 @@ private: double m_dbu; std::string m_record; std::string m_record_returned; + std::list m_sections; void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); std::string resolve_path(const std::string &path); @@ -159,12 +160,15 @@ private: std::string read_record_internal (); void do_read_maly_file (MALYData &data); bool read_maskset (MALYData &data); - void read_mask (MALYReaderMaskData &mask, bool cmask); + void read_mask (MALYReaderMaskData &mask); void read_title (MALYReaderTitleData &mask); void read_parameter (MALYReaderParametersData &mask); void read_strgroup (MALYReaderStrGroupData &mask); db::DTrans extract_title_trans (tl::Extractor &ex); void extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec); + bool begin_section (tl::Extractor &ex, const std::string &name = std::string ()); + bool end_section (tl::Extractor &ex); + void skip_section (); }; } From fa30fd0f7b764a7215b0ccce4113efc343afb2e5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 29 Apr 2025 22:19:59 +0200 Subject: [PATCH 41/76] WIP, MALY reader --- .../streamers/maly/db_plugin/dbMALY.cc | 80 +++++ src/plugins/streamers/maly/db_plugin/dbMALY.h | 31 +- .../streamers/maly/db_plugin/dbMALYReader.cc | 321 ++++++++++++------ .../streamers/maly/db_plugin/dbMALYReader.h | 87 ++++- 4 files changed, 405 insertions(+), 114 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.cc b/src/plugins/streamers/maly/db_plugin/dbMALY.cc index b53b4b449..954514f79 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.cc @@ -38,6 +38,86 @@ MALYDiagnostics::~MALYDiagnostics () // .. nothing yet .. } +// --------------------------------------------------------------- +// MALYData implementation + +std::string +MALYTitle::to_string () const +{ + std::string res; + res += "\"" + string + "\" " + transformation.to_string (); + res += tl::sprintf (" %g,%g,%g", width, height, pitch); + if (font == Standard) { + res += " [Standard]"; + } else if (font == Native) { + res += " [Native]"; + } + return res; +} + +std::string +MALYStructure::to_string () const +{ + std::string res; + res += path + "{" + topcell + "}"; + if (layer < 0) { + res += "(*)"; + } else { + res += tl::sprintf ("(%d)", layer); + } + + if (! mname.empty ()) { + res += " mname(" + mname + ")"; + } + if (! ename.empty ()) { + res += " ename(" + ename + ")"; + } + if (! dname.empty ()) { + res += " dname(" + dname + ")"; + } + + res += " "; + res += size.to_string (); + + res += " "; + res += transformation.to_string (); + + if (nx > 1 || ny > 1) { + res += tl::sprintf (" [%.12gx%d,%.12gx%d]", dx, nx, dy, ny); + } + + return res; +} + +std::string +MALYMask::to_string () const +{ + std::string res; + res += "Mask " + name + "\n"; + res += " Size " + tl::to_string (size_um); + + for (auto t = titles.begin (); t != titles.end (); ++t) { + res += "\n Title " + t->to_string (); + } + for (auto s = structures.begin (); s != structures.end (); ++s) { + res += "\n Ref " + s->to_string (); + } + return res; +} + +std::string +MALYData::to_string () const +{ + std::string res; + for (auto m = masks.begin (); m != masks.end (); ++m) { + if (m != masks.begin ()) { + res += "\n"; + } + res += m->to_string (); + } + return res; +} + // --------------------------------------------------------------- // MALY format declaration diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h index ec904e969..bdcbaae7b 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -76,9 +76,9 @@ public: */ enum Type { - String = 0, // A user-defined string - Date = 1, // The date - Serial = 2 // A serial number + String = 0, // A user-defined string + Date = 1, // The date + Serial = 2 // A serial number }; /** @@ -86,8 +86,9 @@ public: */ enum Font { - Standard = 0, // Standard font - Native = 1 // Native tool font + FontNotSet = 0, // Undef + Standard = 1, // Standard font + Native = 2 // Native tool font }; /** @@ -127,6 +128,11 @@ public: * @brief The font to be used */ Font font; + + /** + * @brief Returns a string representing the structure + */ + std::string to_string () const; }; /** @@ -204,6 +210,11 @@ public: * A value of -1 means "all". */ int layer; + + /** + * @brief Returns a string representing the structure + */ + std::string to_string () const; }; /** @@ -240,6 +251,11 @@ public: * @brief The list of titles */ std::list titles; + + /** + * @brief Returns a string representing the mask + */ + std::string to_string () const; }; /** @@ -258,6 +274,11 @@ public: * @brief The masks defined by the file */ std::list masks; + + /** + * @brief Returns a string representing the data set + */ + std::string to_string () const; }; } diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index ebf2d7b0b..5d9086c48 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -42,88 +42,14 @@ namespace db { -// --------------------------------------------------------------- -// Some helper structures to collect data - -struct MALYReaderTitleSpec -{ - MALYReaderTitleSpec () - : enabled (false), width (1.0), height (1.0), pitch (1.0) - { } - - bool enabled; - db::DTrans trans; - double width, height, pitch; -}; - -struct MALYReaderTitleData -{ - MALYReaderTitleData () - { } - - MALYReaderTitleSpec date_spec; - MALYReaderTitleSpec serial_spec; - std::list > string_titles; -}; - -struct MALYReaderParametersData -{ - MALYReaderParametersData () - : base (Center), array_base (Center), masksize (0.0), maskmirror (false), font (MALYTitle::Standard) - { } - - enum Base - { - Origin, - Center, - LowerLeft - }; - - Base base; - Base array_base; - double masksize; - bool maskmirror; - MALYTitle::Font font; - std::list > roots; -}; - -struct MALYReaderStrRefData -{ - MALYReaderStrRefData () - : layer (-1), scale (1.0), nx (1), ny (1), dx (0.0), dy (0.0) - { } - - std::string file; - std::string name; - int layer; - db::DVector org; - db::DBox size; - double scale; - int nx, ny; - double dx, dy; -}; - -struct MALYReaderStrGroupData -{ - std::string name; - std::list refs; -}; - -struct MALYReaderMaskData -{ - MALYReaderParametersData parameters; - MALYReaderTitleData title; - std::list strgroups; -}; - - // --------------------------------------------------------------- // MALYReader MALYReader::MALYReader (tl::InputStream &s) : m_stream (s), m_progress (tl::to_string (tr ("Reading MALY file")), 1000), - m_dbu (0.001) + m_dbu (0.001), + m_last_record_line (0) { m_progress.set_format (tl::to_string (tr ("%.0fk lines"))); m_progress.set_format_unit (1000.0); @@ -171,6 +97,8 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) MALYData data = read_maly_file (); + // @@@ + std::cout << data.to_string () << std::endl; // @@@ finish_layers (layout); @@ -187,18 +115,26 @@ tl::Extractor MALYReader::read_record () { if (! m_record_returned.empty ()) { + m_record = m_record_returned; + m_record_returned.clear (); + return tl::Extractor (m_record.c_str ()); + } while (! m_stream.at_end ()) { + + m_last_record_line = m_stream.line_number (); m_record = read_record_internal (); + tl::Extractor ex (m_record.c_str ()); if (ex.test ("+")) { error (tl::to_string (tr ("'+' character past first column - did you mean to continue a line?"))); } else if (! ex.at_end ()) { return ex; } + } return tl::Extractor (); @@ -333,12 +269,12 @@ MALYReader::extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec) spec.trans = db::DTrans (rot, ymirror, db::DVector (x, y)); } -static -MALYReaderParametersData::Base string_to_base (const std::string &string) +MALYReader::MALYReaderParametersData::Base +MALYReader::string_to_base (const std::string &string) { if (string == "ORIGIN") { return MALYReaderParametersData::Origin; - } else if (string == "LOWERLEFT") { // @@@? + } else if (string == "LOWERLEFT") { return MALYReaderParametersData::LowerLeft; } else if (string == "CENTER") { return MALYReaderParametersData::Center; @@ -531,7 +467,27 @@ MALYReader::read_strgroup (MALYReaderStrGroupData &data) tl::Extractor ex = read_record (); if (end_section (ex)) { + break; + + } else if (ex.test ("PROPERTY")) { + + if (data.refs.empty ()) { + error (tl::to_string (tr ("PROPERTY entry without a preceeding SREF or AREF"))); + } + + while (! ex.at_end ()) { + if (ex.test ("DNAME")) { + ex.read_word_or_quoted (data.refs.back ().dname); + } else if (ex.test ("ENAME")) { + ex.read_word_or_quoted (data.refs.back ().ename); + } else if (ex.test ("MNAME")) { + ex.read_word_or_quoted (data.refs.back ().mname); + } else { + error (tl::to_string (tr ("Unknown PROPERTY item"))); + } + } + } else if ((is_sref = ex.test ("SREF")) || ex.test ("AREF")) { data.refs.push_back (MALYReaderStrRefData ()); @@ -633,13 +589,15 @@ MALYReader::read_maskset (MALYData &data) if (end_section (ex)) { ex.expect_end (); - // @@@ create_masks (cmask, masks, data); + create_masks (cmask, masks, data); return true; } else if (begin_section (ex, "MASK")) { - ex.expect_end (); masks.push_back (MALYReaderMaskData ()); + ex.read (masks.back ().name); + + ex.expect_end (); read_mask (masks.back ()); } else if (begin_section (ex, "CMASK")) { @@ -654,6 +612,183 @@ MALYReader::read_maskset (MALYData &data) } } +void +MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::list &masks, MALYData &data) +{ + for (auto i = masks.begin (); i != masks.end (); ++i) { + + data.masks.push_back (MALYMask ()); + MALYMask &m = data.masks.back (); + + m.name = i->name; + + m.size_um = i->parameters.masksize * 25400.0; + if (m.size_um < db::epsilon) { + m.size_um = cmask.parameters.masksize * 25400.0; + } + if (m.size_um < db::epsilon) { + m.size_um = 7.0 * 25400.0; // @@@? + } + + MALYTitle::Font font = i->parameters.font; + if (font == MALYTitle::FontNotSet) { + font = cmask.parameters.font; + } + if (font == MALYTitle::FontNotSet) { + font = MALYTitle::Standard; // @@@? + } + + const MALYReaderTitleSpec *date_spec = 0; + if (i->title.date_spec.enabled) { + date_spec = &i->title.date_spec; + } else if (cmask.title.date_spec.enabled) { + date_spec = &cmask.title.date_spec; + } + if (date_spec) { + m.titles.push_back (create_title (MALYTitle::Date, *date_spec, font, std::string (""))); + } + + const MALYReaderTitleSpec *serial_spec = 0; + if (i->title.serial_spec.enabled) { + serial_spec = &i->title.serial_spec; + } else if (cmask.title.serial_spec.enabled) { + serial_spec = &cmask.title.serial_spec; + } + if (date_spec) { + m.titles.push_back (create_title (MALYTitle::Serial, *serial_spec, font, std::string (""))); + } + + for (auto t = i->title.string_titles.begin (); t != i->title.string_titles.end (); ++t) { + m.titles.push_back (create_title (MALYTitle::String, t->second, font, t->first)); + } + for (auto t = cmask.title.string_titles.begin (); t != cmask.title.string_titles.end (); ++t) { + m.titles.push_back (create_title (MALYTitle::String, t->second, font, t->first)); + } + + MALYReaderParametersData::Base base = i->parameters.base; + if (base == MALYReaderParametersData::BaseNotSet) { + base = cmask.parameters.base; + } + if (base == MALYReaderParametersData::BaseNotSet) { + base = MALYReaderParametersData::Center; // @@@? + } + + MALYReaderParametersData::Base array_base = MALYReaderParametersData::BaseNotSet; + if (array_base == MALYReaderParametersData::BaseNotSet) { + array_base = cmask.parameters.base; + } + if (array_base == MALYReaderParametersData::BaseNotSet) { + array_base = MALYReaderParametersData::Center; // @@@? + } + + for (auto sg = cmask.strgroups.begin (); sg != cmask.strgroups.end (); ++sg) { + for (auto s = sg->refs.begin (); s != sg->refs.end (); ++s) { + m.structures.push_back (create_structure (i->parameters, cmask.parameters, *s, sg->name, base, array_base)); + } + } + for (auto sg = i->strgroups.begin (); sg != i->strgroups.end (); ++sg) { + for (auto s = sg->refs.begin (); s != sg->refs.end (); ++s) { + m.structures.push_back (create_structure (i->parameters, cmask.parameters, *s, sg->name, base, array_base)); + } + } + + } +} + +MALYTitle +MALYReader::create_title (MALYTitle::Type type, const MALYReaderTitleSpec &data, MALYTitle::Font font, const std::string &string) +{ + MALYTitle title; + + title.transformation = data.trans; + title.width = data.width; + title.height = data.height; + title.pitch = data.pitch; + title.type = type; + title.font = font; + title.string = string; + + return title; +} + +MALYStructure +MALYReader::create_structure (const MALYReaderParametersData &mparam, const MALYReaderParametersData &cparam, const MALYReaderStrRefData &data, const std::string & /*strgroup_name*/, MALYReaderParametersData::Base base, MALYReaderParametersData::Base array_base) +{ + MALYStructure str; + + str.size = data.size; + str.dname = data.dname; + str.ename = data.ename; + str.mname = data.mname; + str.topcell = data.name; + str.nx = std::max (1, data.nx); + str.ny = std::max (1, data.ny); + str.dx = data.dx; + str.dy = data.dy; + str.layer = data.layer; + + str.path = resolve_path (mparam, data.file); + if (str.path.empty ()) { + str.path = resolve_path (cparam, data.file); + } + if (str.path.empty ()) { + // try any fail later ... + str.path = data.file; + } + + MALYReaderParametersData::Base eff_base = (data.nx > 1 || data.ny > 1) ? array_base : base; + + db::DPoint rp; + switch (eff_base) { + case MALYReaderParametersData::LowerLeft: + rp = data.size.p1 (); + break; + case MALYReaderParametersData::Center: + default: + // NOTE: the center implies the whole array's center in case of an AREF + rp = (data.size + data.size.moved (db::DVector (str.dx * (str.nx - 1), str.dy * (str.ny - 1)))).center (); + break; + case MALYReaderParametersData::Origin: + break; + } + + db::DCplxTrans mirr (mparam.maskmirror != cparam.maskmirror ? db::DFTrans::m90 : db::DFTrans::r0); + str.transformation = mirr * db::DCplxTrans (data.scale, 0.0, false, data.org + (db::DPoint () - rp)); + + return str; +} + +std::string +MALYReader::resolve_path (const MALYReaderParametersData ¶m, const std::string &path) +{ + if (tl::is_absolute (path)) { + + return path; + + } else { + + // NOTE: we don't differentiate by file type here. Each root is used in the + // same way to find the actual file. + // Relative paths are always resolved relative to the MALY file. + + for (auto r = param.roots.begin (); r != param.roots.end (); ++r) { + + std::string p = tl::combine_path (r->second, path); + if (! tl::is_absolute (p)) { + p = tl::combine_path (tl::dirname (m_stream.source ()), p); + } + + if (tl::file_exists (p)) { + return p; + } + + } + + } + + return std::string (); +} + void MALYReader::do_read_maly_file (MALYData &data) { @@ -663,7 +798,7 @@ MALYReader::do_read_maly_file (MALYData &data) } std::string version; - ex.read (version, "."); + ex.read_word (version, "."); // @@@ TODO: what to do with version string? ex.expect_end (); @@ -685,7 +820,7 @@ MALYReader::do_read_maly_file (MALYData &data) void MALYReader::error (const std::string &msg) { - throw MALYReaderException (msg, m_stream.line_number (), m_stream.source ()); + throw MALYReaderException (msg, m_last_record_line, m_stream.source ()); } void @@ -702,7 +837,7 @@ MALYReader::warn (const std::string &msg, int wl) int ws = compress_warning (msg); if (ws < 0) { tl::warn << msg - << tl::to_string (tr (" (line=")) << m_stream.line_number () + << tl::to_string (tr (" (line=")) << m_last_record_line << tl::to_string (tr (", file=")) << m_stream.source () << ")"; } else if (ws == 0) { @@ -710,24 +845,6 @@ MALYReader::warn (const std::string &msg, int wl) } } -std::string -MALYReader::resolve_path (const std::string &path) -{ - tl::URI path_uri (path); - - if (tl::is_absolute (path_uri.path ())) { - - return path_uri.to_string (); - - } else { - - tl::URI source_uri (m_stream.source ()); - source_uri.set_path (tl::dirname (source_uri.path ())); - return source_uri.resolved (tl::URI (path)).to_string (); - - } -} - void MALYReader::do_read (db::Layout &layout, db::cell_index_type cell_index, tl::TextInputStream &stream) { diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index e8544782a..8face4692 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -45,12 +45,6 @@ namespace db { -class MALYReaderMaskData; -class MALYReaderTitleData; -class MALYReaderParametersData; -class MALYReaderStrGroupData; -class MALYReaderTitleSpec; - /** * @brief Generic base class of MALY reader exceptions */ @@ -145,15 +139,89 @@ public: virtual void warn (const std::string &txt, int wl = 1); private: + struct MALYReaderTitleSpec + { + MALYReaderTitleSpec () + : enabled (false), width (1.0), height (1.0), pitch (1.0) + { } + + bool enabled; + db::DTrans trans; + double width, height, pitch; + }; + + struct MALYReaderTitleData + { + MALYReaderTitleData () + { } + + MALYReaderTitleSpec date_spec; + MALYReaderTitleSpec serial_spec; + std::list > string_titles; + }; + + struct MALYReaderParametersData + { + MALYReaderParametersData () + : base (BaseNotSet), array_base (BaseNotSet), masksize (0.0), maskmirror (false), font (MALYTitle::FontNotSet) + { } + + enum Base + { + BaseNotSet, + Origin, + Center, + LowerLeft + }; + + Base base; + Base array_base; + double masksize; + bool maskmirror; + MALYTitle::Font font; + std::list > roots; + }; + + struct MALYReaderStrRefData + { + MALYReaderStrRefData () + : layer (-1), scale (1.0), nx (1), ny (1), dx (0.0), dy (0.0) + { } + + std::string file; + std::string name; + std::string dname, ename, mname; + int layer; + db::DVector org; + db::DBox size; + double scale; + int nx, ny; + double dx, dy; + }; + + struct MALYReaderStrGroupData + { + std::string name; + std::list refs; + }; + + struct MALYReaderMaskData + { + std::string name; + MALYReaderParametersData parameters; + MALYReaderTitleData title; + std::list strgroups; + }; + tl::TextInputStream m_stream; tl::AbsoluteProgress m_progress; double m_dbu; + unsigned int m_last_record_line; std::string m_record; std::string m_record_returned; std::list m_sections; void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); - std::string resolve_path(const std::string &path); MALYData read_maly_file (); tl::Extractor read_record (); void unget_record (); @@ -169,6 +237,11 @@ private: bool begin_section (tl::Extractor &ex, const std::string &name = std::string ()); bool end_section (tl::Extractor &ex); void skip_section (); + MALYTitle create_title (MALYTitle::Type type, const MALYReaderTitleSpec &data, MALYTitle::Font font, const std::string &string); + void create_masks (const MALYReaderMaskData &cmask, const std::list &masks, MALYData &data); + MALYStructure create_structure (const MALYReaderParametersData &mparam, const MALYReaderParametersData &cparam, const MALYReaderStrRefData &data, const std::string &strgroup_name, MALYReaderParametersData::Base base, MALYReaderParametersData::Base array_base); + std::string resolve_path (const MALYReaderParametersData ¶m, const std::string &path); + static MALYReaderParametersData::Base string_to_base (const std::string &string); }; } From 03873d9b6c33b347d58e140254a7611ab8b47fe6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 29 Apr 2025 22:56:53 +0200 Subject: [PATCH 42/76] WIP --- .../streamers/maly/db_plugin/dbMALYReader.cc | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 5d9086c48..189551635 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -253,10 +253,10 @@ MALYReader::extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec) if (ex.test ("MIRROR")) { if (ex.test ("Y")) { ymirror = true; - } else if (ex.test ("OFF")) { // @@@ + } else if (ex.test ("NONE")) { ymirror = false; } else { - error (tl::to_string (tr ("Expected 'Y' or 'OFF' for MIRROR spec"))); + error (tl::to_string (tr ("Expected 'Y' or 'NONE' for MIRROR spec"))); } } @@ -279,8 +279,7 @@ MALYReader::string_to_base (const std::string &string) } else if (string == "CENTER") { return MALYReaderParametersData::Center; } else { - // @@@ error - return MALYReaderParametersData::Center; + error (tl::to_string (tr ("Unknown base specification: ")) + string); } } @@ -391,7 +390,7 @@ MALYReader::read_parameter (MALYReaderParametersData &data) std::string para; ex.read_word_or_quoted (para); - // @@@ TODO: what to do with "para" + // TODO: what to do with "para" ex.expect_end (); @@ -627,7 +626,8 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::listparameters.font; @@ -635,7 +635,7 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::list Date: Tue, 29 Apr 2025 22:59:17 +0200 Subject: [PATCH 43/76] WIP --- src/plugins/streamers/maly/db_plugin/dbMALYReader.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 189551635..29b56c005 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -279,7 +279,7 @@ MALYReader::string_to_base (const std::string &string) } else if (string == "CENTER") { return MALYReaderParametersData::Center; } else { - error (tl::to_string (tr ("Unknown base specification: ")) + string); + throw tl::Exception (tl::to_string (tr ("Unknown base specification: ")) + string); } } @@ -627,7 +627,7 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::listparameters.font; @@ -671,7 +671,7 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::list Date: Wed, 30 Apr 2025 06:39:46 +0900 Subject: [PATCH 44/76] To use Ruby 3.3.8 from MacPorts and fix some Python syntax warnings --- macbuild/build4mac_env.py | 2 +- macbuild/makeDMG4mac.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/macbuild/build4mac_env.py b/macbuild/build4mac_env.py index 3be1c22de..e3527edf6 100755 --- a/macbuild/build4mac_env.py +++ b/macbuild/build4mac_env.py @@ -192,7 +192,7 @@ RubySequoia = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions # install with 'sudo port install ruby33' # [Key Type Name] = 'MP33' Ruby33MacPorts = { 'exe': '/opt/local/bin/ruby3.3', - 'inc': '/opt/local/include/ruby-3.3.7', + 'inc': '/opt/local/include/ruby-3.3.8', 'lib': '/opt/local/lib/libruby.3.3.dylib' } diff --git a/macbuild/makeDMG4mac.py b/macbuild/makeDMG4mac.py index 972a36374..b7e7aa9da 100755 --- a/macbuild/makeDMG4mac.py +++ b/macbuild/makeDMG4mac.py @@ -424,7 +424,7 @@ def CheckPkgDirectory(): #------------------------------------------------------ # [5] Check the occupied disk space #------------------------------------------------------ - command = "\du -sm %s" % DefaultBundleName + command = r"\du -sm %s" % DefaultBundleName sizeApp = int( os.popen(command).read().strip("\n").split("\t")[0] ) #------------------------------------------------------ @@ -671,14 +671,14 @@ def MakeTargetDMGFile(msg=""): imageDest = "%s/.background" % MountDir if not os.path.isdir(imageDest): os.mkdir(imageDest) - command = "\cp -p %s %s/%s" % (imageSrc, imageDest, BackgroundPNG) + command = r"\cp -p %s %s/%s" % (imageSrc, imageDest, BackgroundPNG) os.system(command) #-------------------------------------------------------- # (6) Create a symbolic link to /Applications #-------------------------------------------------------- print( ">>> (6) Creating a symbolic link to /Applications..." ) - command = "\ln -s %s %s/%s" % (RootApplications, MountDir, RootApplications) + command = r"\ln -s %s %s/%s" % (RootApplications, MountDir, RootApplications) os.system(command) #-------------------------------------------------------- @@ -702,7 +702,7 @@ def MakeTargetDMGFile(msg=""): print( ">>> (8) Copying the volume icon..." ) iconsSrc = "macbuild/Resources/%s" % VolumeIcons iconsDest = "%s/.VolumeIcon.icns" % MountDir - command1 = "\cp -p %s %s" % (iconsSrc, iconsDest) + command1 = r"\cp -p %s %s" % (iconsSrc, iconsDest) command2 = "SetFile -c icnC %s" % iconsDest os.system(command1) sleep(2) @@ -713,7 +713,7 @@ def MakeTargetDMGFile(msg=""): # (9) Change the permission #-------------------------------------------------------- print( ">>> (9) Changing permission to 755..." ) - command = "\chmod -Rf 755 %s &> /dev/null" % MountDir + command = r"\chmod -Rf 755 %s &> /dev/null" % MountDir os.system(command) #-------------------------------------------------------- From 55e2b27bf67500d0186be29655081fc703ab9235 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 1 May 2025 23:05:01 +0200 Subject: [PATCH 45/76] WIP, MALY reader --- .../streamers/maly/db_plugin/dbMALYReader.cc | 138 +++++++++++++++--- .../streamers/maly/db_plugin/dbMALYReader.h | 2 +- 2 files changed, 121 insertions(+), 19 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 29b56c005..4c4e38c5b 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -29,6 +29,8 @@ #include "dbStatic.h" #include "dbShapeProcessor.h" #include "dbTechnology.h" +#include "dbCellMapping.h" +#include "dbLayerMapping.h" #include "tlException.h" #include "tlString.h" @@ -92,19 +94,131 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) set_layer_map (specific_options.layer_map); set_create_layers (specific_options.create_other_layers); - // @@@ set_keep_layer_names (specific_options.keep_layer_names); set_keep_layer_names (true); MALYData data = read_maly_file (); - - // @@@ - std::cout << data.to_string () << std::endl; - // @@@ + import_data (layout, data); finish_layers (layout); return layer_map_out (); } +void +MALYReader::import_data (db::Layout &layout, const MALYData &data) +{ + db::LayoutLocker locker (&layout); + + // create a new top cell + db::Cell &top_cell = layout.cell (layout.add_cell ("MALY_JOBDECK")); + + // count the number of files to read + size_t n = 0; + for (auto m = data.masks.begin (); m != data.masks.end (); ++m) { + n += m->structures.size (); + } + + tl::RelativeProgress progress (tl::to_string (tr ("Reading layouts")), n, size_t (1)); + + for (auto m = data.masks.begin (); m != data.masks.end (); ++m, ++progress) { + + db::Cell &mask_cell = layout.cell (layout.add_cell (("MASK_" + m->name).c_str ())); + top_cell.insert (db::CellInstArray (mask_cell.cell_index (), db::Trans ())); + + auto lp = open_layer (layout, m->name); + if (! lp.first) { + continue; + } + unsigned int target_layer = lp.second; + + for (auto s = m->structures.begin (); s != m->structures.end (); ++s) { + + db::LoadLayoutOptions options; + + tl::InputStream is (s->path); + db::Layout temp_layout; + db::Reader reader (is); + reader.read (temp_layout, options); + + // configure MEBES reader for compatibility with OASIS.Mask + try { + options.set_option_by_name ("mebes_produce_boundary", false); + options.set_option_by_name ("mebes_data_layer", s->layer); + options.set_option_by_name ("mebes_data_datatype", int (0)); + } catch (...) { + // ignore if there is no MEBES support + } + + db::cell_index_type source_cell; + + if (s->topcell.empty ()) { + + auto t = temp_layout.begin_top_down (); + if (t == temp_layout.end_top_down ()) { + throw tl::Exception (tl::to_string (tr ("Mask pattern file '%s' does not have a top cell")), s->path); + } + + source_cell = *t; + ++t; + if (t != temp_layout.end_top_down ()) { + throw tl::Exception (tl::to_string (tr ("Mask pattern file '%s' does not have a single top cell")), s->path); + } + + } else { + + auto cbm = layout.cell_by_name (s->topcell.c_str ()); + if (! cbm.first) { + throw tl::Exception (tl::to_string (tr ("Mask pattern file '%s' does not have a cell named '%s' as required by mask '%s'")), s->path, s->topcell, m->name); + } + + source_cell = cbm.second; + + } + + int source_layer = layout.get_layer_maybe (db::LayerProperties (s->layer, 0)); + if (source_layer >= 0) { + + // create a host cell for the pattern + + std::string cn = m->name; + if (s->mname.empty ()) { + cn += ".PATTERN"; + } else { + cn += "." + s->mname; + } + db::cell_index_type target_cell = layout.add_cell (cn.c_str ()); + + // create the pattern instance + + db::ICplxTrans trans = db::CplxTrans (layout.dbu ()).inverted () * s->transformation * db::CplxTrans (temp_layout.dbu ()); + db::CellInstArray array; + if (s->nx > 1 || s->ny > 1) { + db::Coord idx = db::coord_traits::rounded (s->dx / layout.dbu ()); + db::Coord idy = db::coord_traits::rounded (s->dy / layout.dbu ()); + array = db::CellInstArray (target_cell, trans, db::Vector (idx, 0), db::Vector (0, idy), s->nx, s->ny); + } else { + array = db::CellInstArray (target_cell, trans); + } + mask_cell.insert (array); + + // move over the shapes from the pattern layout to the target layout + + db::CellMapping cm; + cm.create_single_mapping_full (layout, target_cell, temp_layout, source_cell); + + db::LayerMapping lm; + lm.map (source_layer, target_layer); + + layout.cell (target_cell).move_tree_shapes (temp_layout.cell (source_cell), cm, lm); + + } + + } + + } + + // @@@ TODO: generate titles +} + void MALYReader::unget_record () { @@ -755,7 +869,7 @@ MALYReader::create_structure (const MALYReaderParametersData &mparam, const MALY } db::DCplxTrans mirr (mparam.maskmirror != cparam.maskmirror ? db::DFTrans::m90 : db::DFTrans::r0); - str.transformation = mirr * db::DCplxTrans (data.scale, 0.0, false, data.org + (db::DPoint () - rp)); + str.transformation = mirr * db::DCplxTrans (data.scale, 0.0, false, data.org) * db::DCplxTrans (db::DPoint () - rp); return str; } @@ -847,17 +961,5 @@ MALYReader::warn (const std::string &msg, int wl) } } -void -MALYReader::do_read (db::Layout &layout, db::cell_index_type cell_index, tl::TextInputStream &stream) -{ - try { - - // @@@ - - } catch (tl::Exception &ex) { - error (ex.msg ()); - } -} - } diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index 8face4692..acbcd8346 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -221,7 +221,7 @@ private: std::string m_record_returned; std::list m_sections; - void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); + void import_data (db::Layout &layout, const MALYData &data); MALYData read_maly_file (); tl::Extractor read_record (); void unget_record (); From 92d79d46c13b74188dac25ed1feb3d2a0c2490cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 21:31:41 +0000 Subject: [PATCH 46/76] Bump pypa/cibuildwheel from 2.23.2 to 2.23.3 Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.23.2 to 2.23.3. - [Release notes](https://github.com/pypa/cibuildwheel/releases) - [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md) - [Commits](https://github.com/pypa/cibuildwheel/compare/v2.23.2...v2.23.3) --- updated-dependencies: - dependency-name: pypa/cibuildwheel dependency-version: 2.23.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 02b0beaba..d9f6a3b9b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,7 +60,7 @@ jobs: HOST_CCACHE_DIR="$(ccache -k cache_dir)" mkdir -p $HOST_CCACHE_DIR - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions - uses: pypa/cibuildwheel@v2.23.2 + uses: pypa/cibuildwheel@v2.23.3 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value From 226ba429f1bc0d3b5f85da73c4cef56ac8717a56 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 2 May 2025 13:27:59 +0200 Subject: [PATCH 47/76] WIP: MALY reader, debugging, tests --- src/plugins/streamers/maly/db_plugin/dbMALY.h | 8 +- .../streamers/maly/db_plugin/dbMALYReader.cc | 55 +++++++--- .../streamers/maly/db_plugin/dbMALYReader.h | 8 +- .../maly/unit_tests/dbMALYReaderTests.cc | 103 ++++++++++++------ testdata/maly/MALY_test1.maly | 60 ++++++++++ testdata/maly/MALY_test10.maly | 62 +++++++++++ .../maly/{MALY_TEST.maly => MALY_test2a.maly} | 2 +- testdata/maly/MALY_test2b.maly | 42 +++++++ testdata/maly/MALY_test2c.maly | 41 +++++++ testdata/maly/MALY_test2d.maly | 41 +++++++ testdata/maly/MALY_test2e.maly | 41 +++++++ testdata/maly/MALY_test2f.maly | 41 +++++++ testdata/maly/MALY_test2g.maly | 41 +++++++ testdata/maly/MALY_test2h.maly | 41 +++++++ testdata/maly/MALY_test2i.maly | 41 +++++++ testdata/maly/MALY_test2j.maly | 41 +++++++ testdata/maly/MALY_test2k.maly | 41 +++++++ testdata/maly/MALY_test2l.maly | 41 +++++++ testdata/maly/MALY_test2m.maly | 41 +++++++ testdata/maly/MALY_test2n.maly | 41 +++++++ testdata/maly/maly_test10_au.oas | Bin 0 -> 1705 bytes testdata/maly/test10_oas/pat.oas | Bin 0 -> 519 bytes 22 files changed, 776 insertions(+), 56 deletions(-) create mode 100644 testdata/maly/MALY_test1.maly create mode 100644 testdata/maly/MALY_test10.maly rename testdata/maly/{MALY_TEST.maly => MALY_test2a.maly} (93%) create mode 100644 testdata/maly/MALY_test2b.maly create mode 100644 testdata/maly/MALY_test2c.maly create mode 100644 testdata/maly/MALY_test2d.maly create mode 100644 testdata/maly/MALY_test2e.maly create mode 100644 testdata/maly/MALY_test2f.maly create mode 100644 testdata/maly/MALY_test2g.maly create mode 100644 testdata/maly/MALY_test2h.maly create mode 100644 testdata/maly/MALY_test2i.maly create mode 100644 testdata/maly/MALY_test2j.maly create mode 100644 testdata/maly/MALY_test2k.maly create mode 100644 testdata/maly/MALY_test2l.maly create mode 100644 testdata/maly/MALY_test2m.maly create mode 100644 testdata/maly/MALY_test2n.maly create mode 100644 testdata/maly/maly_test10_au.oas create mode 100644 testdata/maly/test10_oas/pat.oas diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h index bdcbaae7b..84f2b3347 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -61,7 +61,7 @@ public: /** * @brief A class representing a title field on a mask */ -class MALYTitle +class DB_PUBLIC MALYTitle { public: /** @@ -138,7 +138,7 @@ public: /** * @brief A class representing a structure (pattern) on a mask */ -class MALYStructure +class DB_PUBLIC MALYStructure { public: /** @@ -220,7 +220,7 @@ public: /** * @brief A class representing one mask */ -class MALYMask +class DB_PUBLIC MALYMask { public: /** @@ -261,7 +261,7 @@ public: /** * @brief A class representing the MALY file */ -class MALYData +class DB_PUBLIC MALYData { public: /** diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 4c4e38c5b..562cefba2 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -165,7 +165,7 @@ MALYReader::import_data (db::Layout &layout, const MALYData &data) } else { - auto cbm = layout.cell_by_name (s->topcell.c_str ()); + auto cbm = temp_layout.cell_by_name (s->topcell.c_str ()); if (! cbm.first) { throw tl::Exception (tl::to_string (tr ("Mask pattern file '%s' does not have a cell named '%s' as required by mask '%s'")), s->path, s->topcell, m->name); } @@ -174,27 +174,31 @@ MALYReader::import_data (db::Layout &layout, const MALYData &data) } - int source_layer = layout.get_layer_maybe (db::LayerProperties (s->layer, 0)); + int source_layer = temp_layout.get_layer_maybe (db::LayerProperties (s->layer, 0)); if (source_layer >= 0) { // create a host cell for the pattern std::string cn = m->name; - if (s->mname.empty ()) { - cn += ".PATTERN"; + if (s->topcell.empty ()) { + if (s->mname.empty ()) { + cn += ".PATTERN"; + } else { + cn += "." + s->mname; + } } else { - cn += "." + s->mname; + cn += "." + s->topcell; } db::cell_index_type target_cell = layout.add_cell (cn.c_str ()); // create the pattern instance - db::ICplxTrans trans = db::CplxTrans (layout.dbu ()).inverted () * s->transformation * db::CplxTrans (temp_layout.dbu ()); + db::ICplxTrans trans = db::CplxTrans (layout.dbu ()).inverted () * s->transformation * db::CplxTrans (layout.dbu ()); db::CellInstArray array; if (s->nx > 1 || s->ny > 1) { db::Coord idx = db::coord_traits::rounded (s->dx / layout.dbu ()); db::Coord idy = db::coord_traits::rounded (s->dy / layout.dbu ()); - array = db::CellInstArray (target_cell, trans, db::Vector (idx, 0), db::Vector (0, idy), s->nx, s->ny); + array = db::CellInstArray (target_cell, trans, trans.fp_trans () * db::Vector (idx, 0), trans.fp_trans () * db::Vector (0, idy), s->nx, s->ny); } else { array = db::CellInstArray (target_cell, trans); } @@ -277,6 +281,7 @@ MALYReader::read_record_internal () while (! m_stream.at_end () && (m_stream.get_char () != '*' || m_stream.peek_char () != '/')) ; if (m_stream.at_end ()) { + m_last_record_line = m_stream.line_number (); error (tl::to_string (tr ("/*...*/ comment not closed"))); } m_stream.get_char (); // eat trailing "/" @@ -288,19 +293,25 @@ MALYReader::read_record_internal () } if (c == '\n') { + if (m_stream.peek_char () == '+') { + + if (tl::Extractor (rec.c_str ()).at_end ()) { + m_last_record_line = m_stream.line_number (); + error (tl::to_string (tr ("'+' character at beginning of new record - did you mean to continue a line?"))); + } + // continuation line m_stream.get_char (); // eat "+" if (m_stream.at_end ()) { break; } - c = m_stream.get_char (); + } else { break; } - } - if (c == '"' || c == '\'') { + } else if (c == '"' || c == '\'') { rec += c; @@ -314,16 +325,19 @@ MALYReader::read_record_internal () break; } else if (c == '\\') { if (m_stream.at_end ()) { - error (tl::to_string (tr ("Unexpected end of file inside quotee string"))); + m_last_record_line = m_stream.line_number (); + error (tl::to_string (tr ("Unexpected end of file inside quoted string"))); } c = m_stream.get_char (); rec += c; } else if (c == '\n') { + m_last_record_line = m_stream.line_number (); error (tl::to_string (tr ("Line break inside quoted string"))); } } if (quote) { + m_last_record_line = m_stream.line_number (); error (tl::to_string (tr ("Unexpected end of file inside quotee string"))); } @@ -512,11 +526,14 @@ MALYReader::read_parameter (MALYReaderParametersData &data) std::string format, path; ex.read_word_or_quoted (format); - ex.read_word_or_quoted (path, ".\\/+-"); + ex.read_word_or_quoted (path, ".\\/+-_"); ex.expect_end (); data.roots.push_back (std::make_pair (format, path)); + } else if (begin_section (ex)) { + warn (tl::to_string (tr ("Unknown section ignored"))); + skip_section (); } else { warn (tl::to_string (tr ("Unknown record ignored"))); } @@ -564,6 +581,9 @@ MALYReader::read_title (MALYReaderTitleData &data) ex.expect_end (); + } else if (begin_section (ex)) { + warn (tl::to_string (tr ("Unknown section ignored"))); + skip_section (); } else { warn (tl::to_string (tr ("Unknown record ignored"))); } @@ -639,6 +659,9 @@ MALYReader::read_strgroup (MALYReaderStrGroupData &data) ex.expect_end (); + } else if (begin_section (ex)) { + warn (tl::to_string (tr ("Unknown section ignored"))); + skip_section (); } else { warn (tl::to_string (tr ("Unknown record ignored"))); } @@ -674,6 +697,7 @@ MALYReader::read_mask (MALYReaderMaskData &mask) read_strgroup (mask.strgroups.back ()); } else if (begin_section (ex)) { + warn (tl::to_string (tr ("Unknown section ignored"))); skip_section (); } else { warn (tl::to_string (tr ("Unknown record ignored"))); @@ -718,6 +742,9 @@ MALYReader::read_maskset (MALYData &data) ex.expect_end (); read_mask (cmask); + } else if (begin_section (ex)) { + warn (tl::to_string (tr ("Unknown section ignored"))); + skip_section (); } else { warn (tl::to_string (tr ("Unknown record ignored"))); } @@ -788,9 +815,9 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::listparameters.array_base; if (array_base == MALYReaderParametersData::BaseNotSet) { - array_base = cmask.parameters.base; + array_base = cmask.parameters.array_base; } if (array_base == MALYReaderParametersData::BaseNotSet) { array_base = MALYReaderParametersData::Center; diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index acbcd8346..1cf4787c7 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -138,6 +138,13 @@ public: */ virtual void warn (const std::string &txt, int wl = 1); + /** + * @brief Reads the MALY file into a MALYData structure + * + * This method is provided for test purposes mainly. + */ + MALYData read_maly_file (); + private: struct MALYReaderTitleSpec { @@ -222,7 +229,6 @@ private: std::list m_sections; void import_data (db::Layout &layout, const MALYData &data); - MALYData read_maly_file (); tl::Extractor read_record (); void unget_record (); std::string read_record_internal (); diff --git a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc index cb3f749c3..fdb94953a 100644 --- a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc +++ b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc @@ -24,6 +24,7 @@ #include "dbMALYReader.h" #include "dbLayoutDiff.h" #include "dbWriter.h" +#include "dbTestSupport.h" #include "tlUnitTest.h" #include @@ -54,7 +55,7 @@ static void run_test (tl::TestBase *_this, const std::string &base, const char * options.set_options (opt); db::Manager m (false); - db::Layout layout (&m), layout2 (&m), layout_au (&m); + db::Layout layout (&m); { std::string fn (base); @@ -65,44 +66,74 @@ static void run_test (tl::TestBase *_this, const std::string &base, const char * reader.read (layout, options); } - tl_assert (layout.begin_top_down () != layout.end_top_down ()); - std::string tc_name = layout.cell_name (*layout.begin_top_down ()); + std::string fn_au (base); + fn_au += "/maly/"; + fn_au += file_au; - // normalize the layout by writing to OASIS and reading from .. - - std::string tmp_oas_file = _this->tmp_file (tl::sprintf ("%s.oas", tc_name)); - - { - tl::OutputStream stream (tmp_oas_file); - db::SaveLayoutOptions options; - options.set_format ("OASIS"); - db::Writer writer (options); - writer.write (layout, stream); - } - - { - tl::InputStream stream (tmp_oas_file); - db::Reader reader (stream); - reader.read (layout2); - } - - { - std::string fn (base); - fn += "/maly/"; - fn += file_au; - tl::InputStream stream (fn); - db::Reader reader (stream); - reader.read (layout_au); - } - - bool equal = db::compare_layouts (layout2, layout_au, db::layout_diff::f_boxes_as_polygons | db::layout_diff::f_verbose | db::layout_diff::f_flatten_array_insts, 1); - if (! equal) { - _this->raise (tl::sprintf ("Compare failed after reading - see %s vs %s\n", tmp_oas_file, file_au)); - } + db::compare_layouts (_this, layout, fn_au, db::WriteOAS); } -TEST(1) +TEST(1_Basic) { - run_test (_this, tl::testdata (), "MALY_TEST.maly", "mag_test_au.oas"); + std::string fn (tl::testdata ()); + fn += "/maly/MALY_test1.maly"; + + tl::InputStream stream (fn); + db::MALYReader reader (stream); + + db::MALYData data = reader.read_maly_file (); + + EXPECT_EQ (data.to_string (), + "Mask A\n" + " Size 127000\n" + " Title \"\" m90 50,-50 1,1,1 [Standard]\n" + " Title \"\" r0 0,-50 1,1,1 [Standard]\n" + " Title \"MaskA1\" r0 -50,50 1,1,1 [Standard]\n" + " Title \"WITH \"QUOTES\"\" m45 50,0 1,1,1 [Standard]\n" + " Ref A1.oas{CHIP_A}(1) (0,0;10,10) m90 *1 20,0\n" + " Ref A2.oas{CHIP_A}(2) ename(e001) dname(d001) (0,0;50,50) m90 *0.8 20,0 [2x5,1x2]\n" + " Ref B3.oas{CHIP_A}(2) (0,0;12,12) m90 *1 20,0" + ) +} + +static std::string run_test_with_error (tl::TestBase * /*_this*/, const std::string &file) +{ + std::string fn (tl::testdata ()); + fn += "/maly/"; + fn += file; + + tl::InputStream stream (fn); + db::MALYReader reader (stream); + + try { + reader.read_maly_file (); + tl_assert (false); + } catch (tl::Exception &ex) { + tl::error << ex.msg (); + return ex.msg (); + } +} + +TEST(2_Errors) +{ + EXPECT_EQ (run_test_with_error (_this, "MALY_test2a.maly").find ("Line break inside quoted string (line=17,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2b.maly").find ("/*...*/ comment not closed (line=43,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2c.maly").find ("Expected value STANDARD or NATIVE for FONT (line=7,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2d.maly").find ("Unknown base specification: NOVALIDBASE (line=8,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2e.maly").find ("Expected end of text here: NOVALIDKEY .. (line=15,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2f.maly").find ("Expected 'Y' or 'NONE' for MIRROR spec (line=15,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2g.maly").find ("Expected end of text here: UNEXPECTED (line=20,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2h.maly").find ("Expected value Y or NONE for MASKMIRROR (line=23,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2i.maly").find ("Expected end of text here: UNEXPECTED (line=29,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2j.maly").find ("Expected end of text here: NOVALIDKEY .. (line=30,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2k.maly").find ("Expected a real number here: SCALE 0.80 .. (line=31,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2l.maly").find ("Expected 'PARAMETER' here: CMASK (line=19,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2m.maly").find ("Expected 'CMASK' here: TITLE (line=18,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2n.maly").find ("Header expected ('BEGIN MALY') (line=2, "), size_t (0)); +} + +TEST(10_BasicLayout) +{ + run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_au.oas"); } diff --git a/testdata/maly/MALY_test1.maly b/testdata/maly/MALY_test1.maly new file mode 100644 index 000000000..92512ae14 --- /dev/null +++ b/testdata/maly/MALY_test1.maly @@ -0,0 +1,60 @@ + +// A comment + +/* +A multi-line comment +BEGIN MALY 1.1 -- not seend +*/ + +BEGIN MALY 1.1 // ignored + BEGIN MASKSET + BEGIN CMASK + AN UNKNOWN MASK RECORD + BEGIN NONSENSE + SOMETHING INSIDE A NONSENSE RECORD + BEGIN MORE + SOMETHING INSIDE ANOTHER NONSENSE RECORD + END MORE + END NONSENSE + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE "/home/NATIVE" + REFERENCE TOOL a.para + AN UNKNOWN PARAMETER + END PARAMETER + BEGIN TITLE + AN UNKNOWN TITLE RECORD + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "WITH \"QUOTES\"" 50.0 0 ++SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 ++// with a continuation line + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + +// A comment at the end diff --git a/testdata/maly/MALY_test10.maly b/testdata/maly/MALY_test10.maly new file mode 100644 index 000000000..0e70b1ee2 --- /dev/null +++ b/testdata/maly/MALY_test10.maly @@ -0,0 +1,62 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 7 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK test10_oas + MASKMIRROR NONE + END PARAMETER + END CMASK + BEGIN MASK A + BEGIN STRGROUP G1 + SREF pat.oas TOP 1 ORG -2000.0 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 0.8 + AREF pat.oas TOP 2 ORG 1000 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 1.0 ITERATION 2 5 1500 2000 + END STRGROUP + BEGIN STRGROUP G2 + SREF pat.oas TOP 3 ORG -3000.0 2000 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 2.0 + END STRGROUP + END MASK + BEGIN MASK B + BEGIN PARAMETER + MASKMIRROR Y + END PARAMETER + BEGIN STRGROUP G1 + SREF pat.oas TOP 1 ORG -2000.0 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 0.8 + AREF pat.oas TOP 2 ORG 1000 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 1.0 ITERATION 2 5 1500 2000 + END STRGROUP + BEGIN STRGROUP G2 + SREF pat.oas TOP 3 ORG -3000.0 2000 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 2.0 + END STRGROUP + END MASK + BEGIN MASK C + BEGIN PARAMETER + ARYBASE LOWERLEFT + BASE LOWERLEFT + END PARAMETER + BEGIN STRGROUP G1 + SREF pat.oas TOP 1 ORG -2000.0 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 0.8 + AREF pat.oas TOP 2 ORG 1000 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 1.0 ITERATION 2 5 1500 2000 + END STRGROUP + BEGIN STRGROUP G2 + SREF pat.oas TOP 3 ORG -3000.0 2000 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 2.0 + END STRGROUP + END MASK + BEGIN MASK D + BEGIN PARAMETER + ARYBASE CENTER + BASE CENTER + END PARAMETER + BEGIN STRGROUP G1 + SREF pat.oas TOP 1 ORG -2000.0 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 0.8 + AREF pat.oas TOP 2 ORG 1000 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 1.0 ITERATION 2 5 1500 2000 + END STRGROUP + BEGIN STRGROUP G2 + SREF pat.oas TOP 3 ORG -3000.0 2000 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 2.0 + END STRGROUP + END MASK + END MASKSET +END MALY diff --git a/testdata/maly/MALY_TEST.maly b/testdata/maly/MALY_test2a.maly similarity index 93% rename from testdata/maly/MALY_TEST.maly rename to testdata/maly/MALY_test2a.maly index ca7b71601..53f459c17 100644 --- a/testdata/maly/MALY_TEST.maly +++ b/testdata/maly/MALY_test2a.maly @@ -14,7 +14,7 @@ BEGIN MALY 1.1 BEGIN TITLE DATE 50.0 -50.0 MIRROR Y ROTATE 180 SERIAL 0 -50.0 - STRING TEST 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + STRING "TEST\" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 END TITLE END CMASK BEGIN MASK A diff --git a/testdata/maly/MALY_test2b.maly b/testdata/maly/MALY_test2b.maly new file mode 100644 index 000000000..19d98c940 --- /dev/null +++ b/testdata/maly/MALY_test2b.maly @@ -0,0 +1,42 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + /* not terminated + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2c.maly b/testdata/maly/MALY_test2c.maly new file mode 100644 index 000000000..671061bef --- /dev/null +++ b/testdata/maly/MALY_test2c.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT NOVALIDFONT // wrong keyword + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2d.maly b/testdata/maly/MALY_test2d.maly new file mode 100644 index 000000000..ef775d98a --- /dev/null +++ b/testdata/maly/MALY_test2d.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE NOVALIDBASE // not a valid keyword + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2e.maly b/testdata/maly/MALY_test2e.maly new file mode 100644 index 000000000..212ed0556 --- /dev/null +++ b/testdata/maly/MALY_test2e.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 NOVALIDKEYWORD + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2f.maly b/testdata/maly/MALY_test2f.maly new file mode 100644 index 000000000..1fc9256b5 --- /dev/null +++ b/testdata/maly/MALY_test2f.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR NOVALIDSPEC ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2g.maly b/testdata/maly/MALY_test2g.maly new file mode 100644 index 000000000..041baf271 --- /dev/null +++ b/testdata/maly/MALY_test2g.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A UNEXPECTED + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2h.maly b/testdata/maly/MALY_test2h.maly new file mode 100644 index 000000000..24b8c96a9 --- /dev/null +++ b/testdata/maly/MALY_test2h.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR NOVALIDKEYWORD + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2i.maly b/testdata/maly/MALY_test2i.maly new file mode 100644 index 000000000..5c02ca50e --- /dev/null +++ b/testdata/maly/MALY_test2i.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 UNEXPECTED + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2j.maly b/testdata/maly/MALY_test2j.maly new file mode 100644 index 000000000..7d561956e --- /dev/null +++ b/testdata/maly/MALY_test2j.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 NOVALIDKEYWORD + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2k.maly b/testdata/maly/MALY_test2k.maly new file mode 100644 index 000000000..170e50cab --- /dev/null +++ b/testdata/maly/MALY_test2k.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 // missing argument ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2l.maly b/testdata/maly/MALY_test2l.maly new file mode 100644 index 000000000..1b3e40065 --- /dev/null +++ b/testdata/maly/MALY_test2l.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + // missing closing section END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2m.maly b/testdata/maly/MALY_test2m.maly new file mode 100644 index 000000000..5a9530e3d --- /dev/null +++ b/testdata/maly/MALY_test2m.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + // missing opening BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2n.maly b/testdata/maly/MALY_test2n.maly new file mode 100644 index 000000000..b80eafdad --- /dev/null +++ b/testdata/maly/MALY_test2n.maly @@ -0,0 +1,41 @@ + +BEGIN WRONG 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/maly_test10_au.oas b/testdata/maly/maly_test10_au.oas new file mode 100644 index 0000000000000000000000000000000000000000..89f1533a83498c4122fbf50e6441587ad693bace GIT binary patch literal 1705 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqftG@5`%@xYguLQ5Cs4_D`M3`5Qmrqog z2_eG6%f-tss?3NGVdZ7!l@|OVb8r?95HT{d0F7#R#3^#0g<(N2+rmR~K*Y_kVTPLo zucY7)DZZ62$${8FMlT!C0A6t=1}_)kKZ=C~V*CPz zMw?}39KHOZBlya!*2anN4?HqE#{4`~?w?-X4K2nL9Sarq6dfB7@gt8dg?Xu*j(Ai- z6`OK^NCyM2h~N)V3@0&TxLio^hcIDfM7Uf4>hcP7m(L{C<-CGF_%NKrgyC{-B?fmV z;Xen93&c1OA@#&g_;6F)w$}D14IQ%+5A&FF26DUgvG4B|U-L2lj@HHxyCnmX4k}s- zNS}C-%KVCrftM5F@Pno34(}w@;cS9G*fAW=h~aP+!5^%Il@Sr-Oi-8Cpu2n`sV)~& zVsNzrCOgYQ#s9~?W$N_soZ;_tH7^O{teu>ekd)rg)Xwj3q;Fx{9Qb0k#qTvU6+T>& z)2saOkY`!*^H2s}exQC!;a_Tp4jehhz{cRZLNqs;udIr$>* z+)$_cEH~26vNTTrR`9jAJm1(XXDR~&FNe@~UIr$HEsPDb-!U~z`obvqm%FjEih+ZH zx#i6c?xqO~`GuZyvd@|c0x#`B3W17v8I>694TOIkI|jD3ck_KiQ=ealG=4NMUAVcc zabn}ogBvpxykAHPLmj+OVub($FN5F#F~I};f(JMR4**U0!*r3+b;52w#y}C**yE-u zRlPyF_xg+Eq>_z$#*tvoeZ&zwmj-_NYGHraU0b5h#6=j9a=D!&@GD>Uy<0w~{K+hm1Zo^9E>$~10#PUq))3aZ+Ba0#Y)n4Ig>VaJ=_fKTkXFmF}KjCP4@kdrB X#!H;Ql1!Pgn6Z(Ok)dH^0R{#DaEYlE literal 0 HcmV?d00001 diff --git a/testdata/maly/test10_oas/pat.oas b/testdata/maly/test10_oas/pat.oas new file mode 100644 index 0000000000000000000000000000000000000000..2818a3a183fc24e5bc0de03ad8d6abb978ab10d1 GIT binary patch literal 519 zcmY!lcJ=kt^>+;R4CduxWH!_@U@&5o5oh9Hk>O{S5oh6NP+~~+$vtDu)nFjNdZ5Vh z0&~Z7!OfkGe@;XPt}g!{&2cM=zo+NHGR=)u{H8MxJUDPI`*Pm)GYN6}N^Oh!HqKys zYqNNB?6mWJpH?$xY~A~lnUguhKY*7(RGFE9k!gb81V3IzC5DP@;XelrF6J+iS2jM) zvY)w3>HADct1IDhX)H>4ZI Date: Fri, 2 May 2025 14:25:34 +0200 Subject: [PATCH 48/76] Added title support, tests, debugging --- .../streamers/maly/db_plugin/dbMALYReader.cc | 60 ++++++++++++++---- .../streamers/maly/db_plugin/dbMALYReader.h | 5 +- .../maly/unit_tests/dbMALYReaderTests.cc | 12 ++-- testdata/maly/MALY_test11.maly | 38 +++++++++++ testdata/maly/maly_test11_au.oas | Bin 0 -> 3437 bytes 5 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 testdata/maly/MALY_test11.maly create mode 100644 testdata/maly/maly_test11_au.oas diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 562cefba2..5f4d88fb2 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -31,6 +31,7 @@ #include "dbTechnology.h" #include "dbCellMapping.h" #include "dbLayerMapping.h" +#include "dbGlyphs.h" #include "tlException.h" #include "tlString.h" @@ -218,9 +219,30 @@ MALYReader::import_data (db::Layout &layout, const MALYData &data) } + // produce the titles + + for (auto t = m->titles.begin (); t != m->titles.end (); ++t) { + + const double one_mm = 1000.0; + + auto gen = db::TextGenerator::default_generator (); + double scale = std::min (t->width * one_mm / (gen->width () * gen->dbu ()), t->height * one_mm / (gen->height () * gen->dbu ())); + + auto &s = t->string; + int len = int (s.size ()); + db::DVector shift (-t->width * one_mm * len * 0.5, -t->height * one_mm * 0.5); + double char_spacing = t->width * one_mm - gen->width () * gen->dbu () * scale; + + db::Region text = gen->text_as_region (s, layout.dbu (), scale, false, 0.0, char_spacing, 0.0); + text.transform (db::Trans (db::CplxTrans (layout.dbu ()).inverted () * shift)); + text.transform (db::CplxTrans (layout.dbu ()).inverted () * db::DCplxTrans (t->transformation) * db::CplxTrans (layout.dbu ())); + + text.insert_into (&layout, mask_cell.cell_index (), target_layer); + + } + } - // @@@ TODO: generate titles } void @@ -376,6 +398,10 @@ MALYReader::extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec) ex.read (spec.width); ex.read (spec.height); ex.read (spec.pitch); + } else { + spec.width = 1.0; + spec.height = 1.0; + spec.pitch = 1.0; } if (ex.test ("MIRROR")) { @@ -394,7 +420,7 @@ MALYReader::extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec) rot = (a / 90) % 4; } - spec.trans = db::DTrans (rot, ymirror, db::DVector (x, y)); + spec.trans = db::DTrans (rot, false, db::DVector (x, y)) * db::DTrans (ymirror ? db::DFTrans::m90 : db::DFTrans::r0); } MALYReader::MALYReaderParametersData::Base @@ -552,6 +578,8 @@ MALYReader::read_title (MALYReaderTitleData &data) break; } else if (ex.test ("DATE")) { + data.date_spec.given = true; + if (ex.test ("OFF")) { data.date_spec.enabled = false; } else { @@ -562,6 +590,8 @@ MALYReader::read_title (MALYReaderTitleData &data) } else if (ex.test ("SERIAL")) { + data.serial_spec.given = true; + if (ex.test ("OFF")) { data.serial_spec.enabled = false; } else { @@ -779,31 +809,33 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::listparameters.maskmirror != cmask.parameters.maskmirror); + const MALYReaderTitleSpec *date_spec = 0; - if (i->title.date_spec.enabled) { + if (i->title.date_spec.given) { date_spec = &i->title.date_spec; - } else if (cmask.title.date_spec.enabled) { + } else if (cmask.title.date_spec.given) { date_spec = &cmask.title.date_spec; } - if (date_spec) { - m.titles.push_back (create_title (MALYTitle::Date, *date_spec, font, std::string (""))); + if (date_spec && date_spec->enabled) { + m.titles.push_back (create_title (MALYTitle::Date, *date_spec, font, maskmirror, std::string (""))); } const MALYReaderTitleSpec *serial_spec = 0; - if (i->title.serial_spec.enabled) { + if (i->title.serial_spec.given) { serial_spec = &i->title.serial_spec; - } else if (cmask.title.serial_spec.enabled) { + } else if (cmask.title.serial_spec.given) { serial_spec = &cmask.title.serial_spec; } - if (date_spec) { - m.titles.push_back (create_title (MALYTitle::Serial, *serial_spec, font, std::string (""))); + if (serial_spec && serial_spec->enabled) { + m.titles.push_back (create_title (MALYTitle::Serial, *serial_spec, font, maskmirror, std::string (""))); } for (auto t = i->title.string_titles.begin (); t != i->title.string_titles.end (); ++t) { - m.titles.push_back (create_title (MALYTitle::String, t->second, font, t->first)); + m.titles.push_back (create_title (MALYTitle::String, t->second, font, maskmirror, t->first)); } for (auto t = cmask.title.string_titles.begin (); t != cmask.title.string_titles.end (); ++t) { - m.titles.push_back (create_title (MALYTitle::String, t->second, font, t->first)); + m.titles.push_back (create_title (MALYTitle::String, t->second, font, maskmirror, t->first)); } MALYReaderParametersData::Base base = i->parameters.base; @@ -839,11 +871,11 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::list &masks, MALYData &data); MALYStructure create_structure (const MALYReaderParametersData &mparam, const MALYReaderParametersData &cparam, const MALYReaderStrRefData &data, const std::string &strgroup_name, MALYReaderParametersData::Base base, MALYReaderParametersData::Base array_base); std::string resolve_path (const MALYReaderParametersData ¶m, const std::string &path); diff --git a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc index fdb94953a..438613561 100644 --- a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc +++ b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc @@ -86,10 +86,9 @@ TEST(1_Basic) EXPECT_EQ (data.to_string (), "Mask A\n" " Size 127000\n" - " Title \"\" m90 50,-50 1,1,1 [Standard]\n" - " Title \"\" r0 0,-50 1,1,1 [Standard]\n" - " Title \"MaskA1\" r0 -50,50 1,1,1 [Standard]\n" - " Title \"WITH \"QUOTES\"\" m45 50,0 1,1,1 [Standard]\n" + " Title \"\" m90 0,-50 1,1,1 [Standard]\n" + " Title \"MaskA1\" m90 50,50 1,1,1 [Standard]\n" + " Title \"WITH \"QUOTES\"\" r270 -50,0 1,1,1 [Standard]\n" " Ref A1.oas{CHIP_A}(1) (0,0;10,10) m90 *1 20,0\n" " Ref A2.oas{CHIP_A}(2) ename(e001) dname(d001) (0,0;50,50) m90 *0.8 20,0 [2x5,1x2]\n" " Ref B3.oas{CHIP_A}(2) (0,0;12,12) m90 *1 20,0" @@ -137,3 +136,8 @@ TEST(10_BasicLayout) run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_au.oas"); } +TEST(11_Titles) +{ + run_test (_this, tl::testdata (), "MALY_test11.maly", "maly_test11_au.oas"); +} + diff --git a/testdata/maly/MALY_test11.maly b/testdata/maly/MALY_test11.maly new file mode 100644 index 000000000..4bfc631f1 --- /dev/null +++ b/testdata/maly/MALY_test11.maly @@ -0,0 +1,38 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 7 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK test10_oas + MASKMIRROR NONE + END PARAMETER + BEGIN TITLE + DATE -50000 -5000 MIRROR Y ROTATE 90 + SERIAL -50000 -10000 + STRING "A STRING TITLE UPSIDE DOWN" 0 -50000 MIRROR Y ROTATE 180 + STRING "A STRING TITLE" 0 -51500 + END TITLE + END CMASK + BEGIN MASK A + END MASK + BEGIN MASK B + BEGIN PARAMETER + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + SERIAL OFF + STRING "A STRING TITLE FOR MASK B" 5000 -53000 + END TITLE + END MASK + BEGIN MASK C + BEGIN TITLE + DATE OFF + STRING "A STRING TITLE FOR MASK C" 0 -55000 SIZE 1.5 2.0 3.0 + END TITLE + END MASK + END MASKSET +END MALY diff --git a/testdata/maly/maly_test11_au.oas b/testdata/maly/maly_test11_au.oas new file mode 100644 index 0000000000000000000000000000000000000000..cb76b91c4d0e6a08db546844e97ade3454571b88 GIT binary patch literal 3437 zcmd_pha(h>!@%*oBlBcu@4b#AT)AX(IO~ifC-X?MXYz1l9Vf}os9a?46>)ZCSC^SR zdQLgxF-p9@|Kt6B{)Vp<$`FZAveZQ(P%KP98Yn~suuBgV$jLDtKIIO1eY5j0c!w5#|nmgV%wbU~()Hf3c$O~>@Lp`yc*j1d> zQo6;sxu|gdIyMxGr8^*T0a$3n0o@!+3g9zovY^O3O;X_KmdfQv($v0+18K+nQM37oVb=E>>s-oyh@i^bZb3PR7&=Syw}( zV?<7uBlhp?lsHffS_WFAK_gv9lZvz%(dQ^47wnvLK;u2yPU-ns`p3QIJpA(a$nN`q zuKe1wli31#Bkocj*cLPMb9e?fjkj-`@@epY?{c8g-7@vJmr0HzSibGEKE-E-zlUlv z>Y3_0GD&G9&1jD;NB!Yk9MQ^C1F0-r8F!L#StNkO&Y9V$rXgp)sEr&F7jRK38<(;lT?$e?tao- zrQmFLYRe7ub=GYoD!YpnQmVEKbG0=Pxq_>YFl~HmUWKYKQ0%p^4ulU3P>`18YM0RQ z2PWn$0Nu12YG1>*?-Kcq7iRA{bB{Rb;(gX0JXX`L_7y|<3Ynfd1C zOLP979|@h9uF8GscuM^M^TxDao}%41Xkq5EIi|@rHQUb3KLhi_dL~|SYtQIKJI@WnPtV}fdx#7ED|$!_nLdrTrGbO z&tdAa*EK!uFlMCzs8urc=d%D2$H?POfk(m>ZMCv%31#Y8=~B!^u0iX(-C&KO1V;(T zSAjZt&+000R=!Z?4aobdgka@=F(5~>iHU%$uP(9=BE`*XD_L5M_26Y}$>x~D=gVfe zsVMx`W20?lfVKI7I}es!s1J!!yKho@CXWB%3A2htYCfx5BtP&mChz)+h`u$`tOZmu z;rf&KzmI9t{#M@4euWT!sD=wspXWpkC3N=q2G{KxGUY*{<@6hjfGrOh0HN#j3nP*8>U*( z+ghXf9*PdO2JD5_y6lYX23&WhPKCpoTV93r>}#z=t?Wd7--#gzAN)Dip!7ArQfqrh z@Xq>So>k3dv1dM8T&kM8H|Qf>Fd^9}>we#?lxb=zeXuOyF;@k7)4Nvf)y#u4LJ#wZGnAd9jDr@&TNcElfx z9W8ro#?GM-IsmN;8#9R|Gv$!a+t!osL=xcR4T1Ei>4Su`yN;- z9bj!6)s8Jfp`jk{{cF0EwLltP?Ch{y3D9`ruBXH0BprH;L;Ru6ZHB}c_CckZ6O)l$bO z(ocCjulC>l4|(HT;WnA|@@raaJC8Nu151M-m1x@*heQO&E189Srs*~i>s`b)%LrM> zVGO=2g&6%$DOeKjvMup=XpX-42o1pRfA$t{#KbJq)4^228%7OJ=@gG;6@ zcl?=})!sXVna43OgnCP94XA@9e0R4pudjUKu<)v33{jK4ig~Rd3DDnkPrn!9L!WkTm!sW5RrM<|PlSh*m{gGpQzVM=Z zc=G1x#Qu!>R&Kt#WJru=zd+b|o|Mlfhy8_dsr#r#&@_dp_-Ca3h;FtixQ{~gOV;H3 z<hHhRybLeXAArYdU3XO;(zXp} zV(A0-A)$at?U^U3u}KJL0M3z*7AC9UFx{xczEO;dW3-(=)*Y@rHfqJ#0M3twfE<0E zUGOpZ2fKfL%0<2MxdPa1NrT$a1({$)%Yqy=_foDVu=G~a&8MteMIUacjxsq|G?IhnyQ!k(D{Pu8WC7Vy*akH zk=*1oT&gO4nTNb}G=UVIxR~FYt`cbDd=vcQ;S45m#fnP2d>k0HWg4$F3N~mPoTw7p znJ&JehVmfeC5HGgLc|C56$lHB?@I*rP7UW@pDaJ$P%BO!P3X*m@69~Pw#wW85v&)b zlNY?lTySH^Oe%-JkU2!&nuo!)Kawqn36fiQ`ZZZYER2Igz3ufw@HwZzN)1_@Q|SB1 zV$c&|>j+~Luh>?cPM{Z9B3|b(n=K+PW~&b8cGNT3!DB zp|FNLqka(DU&Vj34HjNzMsX+3FfbIOhD4*5Wq`}d$R;F;4Q0gQD98R;fA&!1U8TWH zi=^1FtcevflfZ;i>;1ljm0LtB1el6@7>%! z{^Fzy#Q{7QOuWVbU!JO931hzH!u&$koK4?24m$XN)XZOV2L18Dk?B1(r<-YN43h$oKKEwJ?o~MUJI^FgV*(tuk cdmkA9AmDpmAOI)<3<2VRKtSC8uK Date: Fri, 2 May 2025 14:49:13 +0200 Subject: [PATCH 49/76] Providing meta info (boundary per mask) from MALY reader --- .../streamers/gds2/db_plugin/dbGDS2ReaderBase.cc | 2 -- src/plugins/streamers/maly/db_plugin/dbMALY.cc | 2 +- .../streamers/maly/db_plugin/dbMALYReader.cc | 15 +++++++++++++++ .../streamers/maly/db_plugin/dbMALYReader.h | 1 + 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index 413db5084..dad91a563 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -33,8 +33,6 @@ namespace db { -// --------------------------------------------------------------- - // --------------------------------------------------------------- // GDS2ReaderBase diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.cc b/src/plugins/streamers/maly/db_plugin/dbMALY.cc index 954514f79..385e217fa 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.cc @@ -133,7 +133,7 @@ public: virtual std::string format_name () const { return "MALY"; } virtual std::string format_desc () const { return "MALY jobdeck"; } virtual std::string format_title () const { return "MALY (MALY jobdeck format)"; } - virtual std::string file_format () const { return "MALY jobdeck files (*.maly *.MALY)"; } + virtual std::string file_format () const { return "MALY jobdeck files (*.maly *.MALY *.mly *.MLY)"; } virtual bool detect (tl::InputStream &s) const { diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 5f4d88fb2..d57efcd59 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -99,11 +99,26 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) MALYData data = read_maly_file (); import_data (layout, data); + create_metadata (layout, data); finish_layers (layout); return layer_map_out (); } +void +MALYReader::create_metadata (db::Layout &layout, const MALYData &data) +{ + tl::Variant boundary_per_mask = tl::Variant::empty_array (); + + for (auto m = data.masks.begin (); m != data.masks.end (); ++m) { + double ms = m->size_um; + db::DBox box (-0.5 * ms, -0.5 * ms, 0.5 * ms, 0.5 * ms); + boundary_per_mask.insert (m->name, box); + } + + layout.add_meta_info ("boundary_per_mask", MetaInfo (tl::to_string (tr ("Physical mask boundary per mask name")), boundary_per_mask)); +} + void MALYReader::import_data (db::Layout &layout, const MALYData &data) { diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index bf6ed2989..ea4feabbb 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -230,6 +230,7 @@ private: std::list m_sections; void import_data (db::Layout &layout, const MALYData &data); + void create_metadata (db::Layout &layout, const MALYData &data); tl::Extractor read_record (); void unget_record (); std::string read_record_internal (); From b77b4d7d3c71a24308fdea91dbb60b1919dd6492 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 2 May 2025 15:02:18 +0200 Subject: [PATCH 50/76] Bug fixes --- src/plugins/streamers/maly/db_plugin/dbMALYReader.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index d57efcd59..a474874a7 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -88,8 +88,6 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) { init (options); - prepare_layers (layout); - const db::MALYReaderOptions &specific_options = options.get_options (); m_dbu = specific_options.dbu; @@ -97,6 +95,8 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) set_create_layers (specific_options.create_other_layers); set_keep_layer_names (true); + prepare_layers (layout); + MALYData data = read_maly_file (); import_data (layout, data); create_metadata (layout, data); From 1932532416a6fedfd0e2567e0c76974e2092c620 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 2 May 2025 15:07:28 +0200 Subject: [PATCH 51/76] Bug fixes, tests --- src/plugins/streamers/maly/db_plugin/dbMALY.cc | 2 +- .../maly/unit_tests/dbMALYReaderTests.cc | 1 + testdata/maly/maly_test10_lm_au.oas | Bin 0 -> 1705 bytes 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 testdata/maly/maly_test10_lm_au.oas diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.cc b/src/plugins/streamers/maly/db_plugin/dbMALY.cc index 385e217fa..372b91f2d 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.cc @@ -163,7 +163,7 @@ public: virtual tl::XMLElementBase *xml_reader_options_element () const { - return new db::ReaderOptionsXMLElement ("mag", + return new db::ReaderOptionsXMLElement ("maly", tl::make_member (&db::MALYReaderOptions::dbu, "dbu") + tl::make_member (&db::MALYReaderOptions::layer_map, "layer-map") + tl::make_member (&db::MALYReaderOptions::create_other_layers, "create-other-layers") diff --git a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc index 438613561..95a6ff447 100644 --- a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc +++ b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc @@ -134,6 +134,7 @@ TEST(2_Errors) TEST(10_BasicLayout) { run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_au.oas"); + run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_lm_au.oas", "A: 10, B: 11, C: 12, D: 13"); } TEST(11_Titles) diff --git a/testdata/maly/maly_test10_lm_au.oas b/testdata/maly/maly_test10_lm_au.oas new file mode 100644 index 0000000000000000000000000000000000000000..5d495f5b1ab34757a7892a6949e138bc9fae5b11 GIT binary patch literal 1705 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqftG@5`%@xvym(W8jq$Rpw=2WI8a52Z(s3cqMp6MU{CFBEr0aynLd{ z+z1gKUM^mCQDrWK2rDl$ue9I~nS--mBTkX~ybKF^*%ls>10rsQ4Kv&% zcqIjYND)>hYQf0y;CJzYHU5oTB^G`+1L|LJNDjmXGJ4s72Jnh2F?hKM|4}R~5aSmx zG}dm|{=g%%W6aM(<^JjA-Oyr8(XmirPtmaf5kK4--a zRIw=sh;%UUiU|G?#c&c2hRcNne+Uy+Muf`+pf0aKcX>OhF6R~e!H3}_ZVZ=mD>1k` z3I91*Tp-4Q2&pG_=7*c&wzalDY3P`pc$mkWGmzV@k9~i)_?nOTceFNs*ew~5bWqV! zK>EasROVM~47{8WhaW6Oclc~l9nL2BgB`=+To?{#5&XeQSQ!yP&IEON4Z6#_Np-oH z5`(K1FxgoaD*iwAEmNn5=L~F zoL=RJhdj%gpNBH=@&ol-3jb0&bl}K223GdvxhYl*N1E(g#OBO9xZ}YD8D-|r&B+&e z=Y~4nXStDnmZfp}w}P*|<@v^DIa3)JcsYc=^D;0oY+-De{f?<&(icX-zub+TRSX;q z%q?$ra5qg@$S?GqlYQ1q5O`@1QV3MU%c#U)Zy@~Z*fFrJy_@eFn)>`Yr17J9>B7xj zjT0Mx9^9Cr;Qc~U80z4K5-S84co_r_hzTCx7d*frcmQa^AEt|pt`m0iF$Rjb#vV6S zsp<{Vz1LqPCzWg@#HD4glGW*=Z|C6Rt++^#v$I2EiqgLWciXKmueE)BCzNNY&1PGP zZ8fJeZ{?}Udge?L`F>`dwaLa~o|DqnJuk12Q2EucU7>k@8uyIb7J>iU*B=luOwW;c z|LFeK4d*x~O?x*_Wu4c7*;V Date: Sun, 4 May 2025 23:06:22 +0200 Subject: [PATCH 52/76] Updating changelog for 0.30.1 --- Changelog | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index 387183e50..8357044b8 100644 --- a/Changelog +++ b/Changelog @@ -1,16 +1,17 @@ 0.30.1 (2025-04-27): +* Bugfix: %GITHUB%/issues/2038 DEdgePairWithProperties not working properly in scripts * Bugfix: %GITHUB%/issues/2011 Some DRC bugs fixed * Bug: %GITHUB%/issues/2014 Bug fixes in LEF/DEF reader * Enhancement: %GITHUB%/issues/2016 Lazy evaluation of PCell also when changing guiding shape properties * Enhancement: %GITHUB%/issues/2019 Support for Qt 6.9 -* Bugfix: %GITHUB%/issues/2020 String weak ordering issue fixed in edge processor +* Bugfix: %GITHUB%/issues/2020 Strict weak ordering issue fixed in edge processor * Enhancement: %GITHUB%/issues/2024 Option to configure grid density * Bugfix: %GITHUB%/issues/2025 Brackets get added in List type PCell parameter edit field * Bugfix: %GITHUB%/issues/2026 Display is dead after opening 2.5d view * Bugfix/Enhancement: some updates of "strmxor" tool - strmxor was giving wrong results if cell variants are present where one variant is covered entirely by a large shape - - parallelization now happens on a per-layer basis (same as for + - Parallelization now happens on a per-layer basis (same as for XOR tool in KLayout) - Shape count was not consistent in deep mode - All buddy tools print total runtime with -d11 From 6d871166f72803db142788bdf93ba1d414a90d79 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 May 2025 23:12:47 +0200 Subject: [PATCH 53/76] Updating changelog for 0.30.2 --- Changelog | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 8357044b8..1138f86d5 100644 --- a/Changelog +++ b/Changelog @@ -1,8 +1,10 @@ +0.30.2 (2025-xx-xx): +* Enhancement: %GITHUB%/issues/2016 Lazy evaluation of PCell also when changing guiding shape properties + 0.30.1 (2025-04-27): * Bugfix: %GITHUB%/issues/2038 DEdgePairWithProperties not working properly in scripts * Bugfix: %GITHUB%/issues/2011 Some DRC bugs fixed * Bug: %GITHUB%/issues/2014 Bug fixes in LEF/DEF reader -* Enhancement: %GITHUB%/issues/2016 Lazy evaluation of PCell also when changing guiding shape properties * Enhancement: %GITHUB%/issues/2019 Support for Qt 6.9 * Bugfix: %GITHUB%/issues/2020 Strict weak ordering issue fixed in edge processor * Enhancement: %GITHUB%/issues/2024 Option to configure grid density From e232bf8127dbda94a7a54aa63b92722b33ca86cb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 10 May 2025 19:59:19 +0000 Subject: [PATCH 54/76] Specify build system in pyproject.toml, using SPDX compliant license spec. --- pyproject.toml | 4 ++++ setup.py | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index eafccb77f..a436bf5c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,7 @@ +[build-system] +requires = ["setuptools >= 77.0.3"] +build-backend = "setuptools.build_meta" + [tool.cibuildwheel] build-verbosity = "3" test-command = [ diff --git a/setup.py b/setup.py index c816a0c5e..04fb5c4f9 100644 --- a/setup.py +++ b/setup.py @@ -987,7 +987,7 @@ if __name__ == "__main__": setup( name=config.root, version=config.version(), - license="GNU GPLv3", + license="GPL-3.0-or-later", description="KLayout standalone Python package", long_description="This package is a standalone distribution of KLayout's Python API.\n\nFor more details see here: https://www.klayout.org/klayout-pypi", author="Matthias Koefferlein", @@ -996,7 +996,6 @@ if __name__ == "__main__": # Recommended classifiers "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", From e279888f52f43be514361db6c806f9045782fe99 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Sun, 11 May 2025 09:07:55 -0400 Subject: [PATCH 55/76] Use Py_IncRef in pyaCallables.cc --- src/pya/pya/pyaCallables.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pya/pya/pyaCallables.cc b/src/pya/pya/pyaCallables.cc index 829f7076f..27f5fa790 100644 --- a/src/pya/pya/pyaCallables.cc +++ b/src/pya/pya/pyaCallables.cc @@ -49,7 +49,7 @@ pya_object_deallocate (PyObject *self) // may trigger a GC (https://github.com/KLayout/klayout/issues/1054). // According to the comments this may be turned into a release mode assertion, so // we better work around it. - ++self->ob_refcnt; + Py_IncRef(self); // Mute Python warnings in debug case PyObject_GC_UnTrack (self); From 7bee9ebcec43f90e5eb08dddb770f532add0a600 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 11 May 2025 21:48:01 +0200 Subject: [PATCH 56/76] Fixing a linker issue on Windows. --- src/plugins/streamers/maly/db_plugin/dbMALY.h | 9 +++++---- src/plugins/streamers/streamers.pro | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h index 84f2b3347..32a5e07a7 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -27,6 +27,7 @@ #include "dbPoint.h" #include "dbTrans.h" #include "dbBox.h" +#include "dbPluginCommon.h" #include "tlException.h" #include "tlInternational.h" @@ -61,7 +62,7 @@ public: /** * @brief A class representing a title field on a mask */ -class DB_PUBLIC MALYTitle +class DB_PLUGIN_PUBLIC MALYTitle { public: /** @@ -138,7 +139,7 @@ public: /** * @brief A class representing a structure (pattern) on a mask */ -class DB_PUBLIC MALYStructure +class DB_PLUGIN_PUBLIC MALYStructure { public: /** @@ -220,7 +221,7 @@ public: /** * @brief A class representing one mask */ -class DB_PUBLIC MALYMask +class DB_PLUGIN_PUBLIC MALYMask { public: /** @@ -261,7 +262,7 @@ public: /** * @brief A class representing the MALY file */ -class DB_PUBLIC MALYData +class DB_PLUGIN_PUBLIC MALYData { public: /** diff --git a/src/plugins/streamers/streamers.pro b/src/plugins/streamers/streamers.pro index 5d7bbf3ad..8bfcea845 100644 --- a/src/plugins/streamers/streamers.pro +++ b/src/plugins/streamers/streamers.pro @@ -6,4 +6,3 @@ SUBDIR_LIST = $$files($$PWD/*) SUBDIR_LIST -= $$PWD/streamers.pro SUBDIRS = $$SUBDIR_LIST - From f9e2a9257fd11f2a67ff725c8c0c1eb548a23b76 Mon Sep 17 00:00:00 2001 From: Henner Zeller Date: Mon, 19 May 2025 14:15:31 +0200 Subject: [PATCH 57/76] Fix logic error in hex2int. This was probably never noticed as it was the last branch and would behave benign on valid input. Signed-off-by: Henner Zeller --- src/tl/tl/tlUri.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tl/tl/tlUri.cc b/src/tl/tl/tlUri.cc index d0a6aade3..8032a2cc2 100644 --- a/src/tl/tl/tlUri.cc +++ b/src/tl/tl/tlUri.cc @@ -41,7 +41,7 @@ static char hex2int (char c) return c - '0'; } else if (c >= 'A' && c <= 'F') { return (c - 'A') + 10; - } else if (c >= 'a' || c <= 'f') { + } else if (c >= 'a' && c <= 'f') { return (c - 'a') + 10; } else { return 0; From 6ee824cb3cd6cf529e73ae72fa91527a29d3c66b Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Wed, 21 May 2025 16:02:33 +0900 Subject: [PATCH 58/76] adding arm linux to os matrix --- .github/workflows/build.yml | 52 ++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9f6a3b9b..a49910d91 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,10 @@ jobs: - os: "ubuntu-latest" cibuild: "*musllinux*" cibw_arch: "musllinux" + - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner + cibuild: "*manylinux*" + cibw_arch: "manylinux" + steps: - name: Free Disk Space (Ubuntu) if: matrix.os == 'ubuntu-latest' @@ -90,43 +94,43 @@ jobs: name: Make SDist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Build SDist - run: pipx run build --sdist + - name: Build SDist + run: pipx run build --sdist - - uses: actions/upload-artifact@v4 - with: - name: artifact-sdist - path: dist/*.tar.gz + - uses: actions/upload-artifact@v4 + with: + name: artifact-sdist + path: dist/*.tar.gz upload_to_test_pypy: needs: [build, make_sdist] runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - path: dist + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: dist - - uses: pypa/gh-action-pypi-publish@v1.12.4 + - uses: pypa/gh-action-pypi-publish@v1.12.4 continue-on-error: true # might fail if we don't bump the version - with: - user: __token__ - password: ${{ secrets.test_pypi_password }} - repository-url: https://test.pypi.org/legacy/ + with: + user: __token__ + password: ${{ secrets.test_pypi_password }} + repository-url: https://test.pypi.org/legacy/ upload_to_pypi: needs: [build, make_sdist] runs-on: ubuntu-latest if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - path: dist + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: dist - - uses: pypa/gh-action-pypi-publish@v1.12.4 - with: - user: __token__ - password: ${{ secrets.pypi_password }} + - uses: pypa/gh-action-pypi-publish@v1.12.4 + with: + user: __token__ + password: ${{ secrets.pypi_password }} From 6d28c78eeb59cd4d63d8e344c81aca8a347862bd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 22 May 2025 22:48:48 +0200 Subject: [PATCH 59/76] Fixed issue #2060 - the precision of the check was too low. --- src/db/db/dbEdgesUtils.cc | 10 +++++----- src/db/db/dbEdgesUtils.h | 2 +- src/db/unit_tests/dbEdgesTests.cc | 13 +++++++++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index 15dcb5a1b..382c378bd 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -250,8 +250,8 @@ EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start include_angle_start = true; } - m_t_start = db::CplxTrans(1.0, angle_start, false, db::DVector ()); - m_t_end = db::CplxTrans(1.0, angle_end, false, db::DVector ()); + m_t_start = db::ICplxTrans (1.0, angle_start, false, db::Vector ()); + m_t_end = db::ICplxTrans (1.0, angle_end, false, db::Vector ()); m_include_start = include_angle_start; m_include_end = include_angle_end; @@ -266,10 +266,10 @@ EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start bool EdgeAngleChecker::check (const db::Vector &a, const db::Vector &b) const { - db::DVector vout (b); + db::Vector vout (b); - db::DVector v1 = m_t_start * a; - db::DVector v2 = m_t_end * a; + db::Vector v1 = m_t_start * a; + db::Vector v2 = m_t_end * a; int vps1 = db::vprod_sign (v1, vout); int vps2 = db::vprod_sign (v2, vout); diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index d0082806d..769687b01 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -155,7 +155,7 @@ public: } private: - db::CplxTrans m_t_start, m_t_end; + db::ICplxTrans m_t_start, m_t_end; bool m_include_start, m_include_end; bool m_big_angle, m_all; bool m_inverse, m_absolute; diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index 0db2b9676..662aceb7b 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -261,6 +261,19 @@ TEST(4) db::EdgeOrientationFilter f1 (89.0, true, 90.0, false, false, false); EXPECT_EQ (r.filtered (f1).to_string (), ""); } + + // issue-2060 + { + db::EdgeOrientationFilter f1 (90.0, true, false); + + db::Edges rr; + rr.insert (db::Box (db::Point (0, 0), db::Point (1000, 4000000))); + EXPECT_EQ (db::compare (rr.filtered (f1), "(1000,0;0,0);(0,4000000;1000,4000000)"), true); + + rr.clear (); + rr.insert (db::Box (db::Point (0, 0), db::Point (1000, 400000))); + EXPECT_EQ (db::compare (rr.filtered (f1), "(1000,0;0,0);(0,400000;1000,400000)"), true); + } } TEST(5) From 3e69da8911db13cb05763a4161fa6b3e02f724df Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 10:41:25 +0900 Subject: [PATCH 60/76] reverting build.yml --- .github/workflows/build.yml | 52 +++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a49910d91..d9f6a3b9b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,10 +34,6 @@ jobs: - os: "ubuntu-latest" cibuild: "*musllinux*" cibw_arch: "musllinux" - - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner - cibuild: "*manylinux*" - cibw_arch: "manylinux" - steps: - name: Free Disk Space (Ubuntu) if: matrix.os == 'ubuntu-latest' @@ -94,43 +90,43 @@ jobs: name: Make SDist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Build SDist - run: pipx run build --sdist + - name: Build SDist + run: pipx run build --sdist - - uses: actions/upload-artifact@v4 - with: - name: artifact-sdist - path: dist/*.tar.gz + - uses: actions/upload-artifact@v4 + with: + name: artifact-sdist + path: dist/*.tar.gz upload_to_test_pypy: needs: [build, make_sdist] runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - path: dist + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: dist - - uses: pypa/gh-action-pypi-publish@v1.12.4 + - uses: pypa/gh-action-pypi-publish@v1.12.4 continue-on-error: true # might fail if we don't bump the version - with: - user: __token__ - password: ${{ secrets.test_pypi_password }} - repository-url: https://test.pypi.org/legacy/ + with: + user: __token__ + password: ${{ secrets.test_pypi_password }} + repository-url: https://test.pypi.org/legacy/ upload_to_pypi: needs: [build, make_sdist] runs-on: ubuntu-latest if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - path: dist + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: dist - - uses: pypa/gh-action-pypi-publish@v1.12.4 - with: - user: __token__ - password: ${{ secrets.pypi_password }} + - uses: pypa/gh-action-pypi-publish@v1.12.4 + with: + user: __token__ + password: ${{ secrets.pypi_password }} From fc00191f5440b3f1bba56815b5322e4e6b90afb6 Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 10:44:57 +0900 Subject: [PATCH 61/76] adding arm config back to test matrix --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9f6a3b9b..ceaa6b5c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,9 @@ jobs: - os: "ubuntu-latest" cibuild: "*musllinux*" cibw_arch: "musllinux" + - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner + cibuild: "*manylinux*" + cibw_arch: "manylinux" steps: - name: Free Disk Space (Ubuntu) if: matrix.os == 'ubuntu-latest' From 8f96e14a45ed64f2900a79cf08e862f083b04cdd Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 10:56:04 +0900 Subject: [PATCH 62/76] trying the job in a new workflow i can run --- .github/workflows/try_build.yml | 83 +++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .github/workflows/try_build.yml diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml new file mode 100644 index 000000000..f5c25b543 --- /dev/null +++ b/.github/workflows/try_build.yml @@ -0,0 +1,83 @@ +name: Build Python Wheels +# https://docs.github.com/en/free-pro-team@latest/actions/guides/building-and-testing-python + +on: + # we just want to test in this PR + pull_request: + + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + max-parallel: 12 + matrix: + include: + - os: "macos-13" # intel runner + cibuild: "*macosx*" + cibw_arch: "macos_x86_64" + macos-arch: "x86_64" + - os: "macos-14" # M1 runner + cibuild: "*macosx*" + cibw_arch: "macos_arm64" + macos-arch: "arm64" + - os: "ubuntu-latest" + cibuild: "*manylinux*" + cibw_arch: "manylinux" + - os: "ubuntu-latest" + cibuild: "*musllinux*" + cibw_arch: "musllinux" + - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner + cibuild: "*manylinux*" + cibw_arch: "manylinux" + steps: + - name: Free Disk Space (Ubuntu) + if: matrix.os == 'ubuntu-latest' + uses: jlumbroso/free-disk-space@main + with: + android: true + dotnet: true + haskell: true + large-packages: true + - uses: hmarr/debug-action@v3 + - name: Cancel Workflow Action + uses: styfle/cancel-workflow-action@0.12.1 + - uses: actions/checkout@v4 + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS + max-size: "5G" + - name: Install dependencies + run: | + env + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH + HOST_CCACHE_DIR="$(ccache -k cache_dir)" + mkdir -p $HOST_CCACHE_DIR + - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions + uses: pypa/cibuildwheel@v2.23.3 + # to supply options, put them in 'env', like: + # env: + # CIBW_SOME_OPTION: value + env: + CIBW_BUILD: ${{ matrix.cibuild }} + CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }} + CIBW_DEPENDENCY_VERSIONS_MACOS: cibw_constraints.txt + + - name: Download Cache from Docker (linux only) + if: ${{ runner.os == 'Linux' }} and ${{ matrix.os != 'ubuntu-24.04-arm' }} + # hack until https://github.com/pypa/cibuildwheel/issues/1030 is fixed + run: | + env + ccache -s + HOST_CCACHE_DIR="$(ccache -k cache_dir)" + rm -rf $HOST_CCACHE_DIR + mv ./wheelhouse/.ccache $HOST_CCACHE_DIR + ls -la $HOST_CCACHE_DIR + ccache -s + - uses: actions/upload-artifact@v4 + with: + name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl \ No newline at end of file From 5ee1ccd0e267886cf8c8f695cbb84c647b35c2ea Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 10:57:56 +0900 Subject: [PATCH 63/76] see if i can build without cache --- .github/workflows/try_build.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index f5c25b543..92be2b359 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -44,11 +44,6 @@ jobs: - name: Cancel Workflow Action uses: styfle/cancel-workflow-action@0.12.1 - uses: actions/checkout@v4 - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS - max-size: "5G" - name: Install dependencies run: | env From ae5503e5db14478e762a18c65789661fdb54719e Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 11:00:18 +0900 Subject: [PATCH 64/76] try to run on my own fork --- .github/workflows/try_build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index 92be2b359..1a1c7ad9e 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -2,8 +2,9 @@ name: Build Python Wheels # https://docs.github.com/en/free-pro-team@latest/actions/guides/building-and-testing-python on: - # we just want to test in this PR - pull_request: + push: + branches: + - support-arm-linux jobs: From 793f29296ebba10f1fa6c540bfc60157fe4065a1 Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 11:01:55 +0900 Subject: [PATCH 65/76] adding ccache job back --- .github/workflows/try_build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index 1a1c7ad9e..4d7e7e691 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -45,6 +45,11 @@ jobs: - name: Cancel Workflow Action uses: styfle/cancel-workflow-action@0.12.1 - uses: actions/checkout@v4 + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS + max-size: "5G" - name: Install dependencies run: | env From 30362cc6bf59c8755cbf884fe0859cffca273637 Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 11:16:34 +0900 Subject: [PATCH 66/76] skipping ccache for arm again --- .github/workflows/try_build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index 4d7e7e691..5e362b824 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -46,11 +46,13 @@ jobs: uses: styfle/cancel-workflow-action@0.12.1 - uses: actions/checkout@v4 - name: ccache + if: matrix.os != 'ubuntu-24.04-arm' uses: hendrikmuhs/ccache-action@v1.2 with: key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS max-size: "5G" - name: Install dependencies + if: matrix.os != 'ubuntu-24.04-arm' run: | env export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" From 30ef907f4cb11813f1b0351eb0507f5fecb211b7 Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 11:34:54 +0900 Subject: [PATCH 67/76] breaking out arm build job --- .github/workflows/try_build.yml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index 5e362b824..efb3161dd 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -59,11 +59,24 @@ jobs: echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH HOST_CCACHE_DIR="$(ccache -k cache_dir)" mkdir -p $HOST_CCACHE_DIR - - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions + - name: Build wheels (ARM) + if: matrix.os == 'ubuntu-24.04-arm' + uses: pypa/cibuildwheel@v2.23.3 + env: + # override the default CentOS “yum install … ccache” and drop ccache + CIBW_BEFORE_ALL_LINUX: | + yum install -y \ + zlib-devel \ + curl-devel \ + expat-devel \ + libpng-devel + CIBW_BUILD: ${{ matrix.cibuild }} + CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }} + # … any other cibuildwheel env you already have … + + - name: Build wheels (all other platforms) + if: matrix.os != 'ubuntu-24.04-arm' uses: pypa/cibuildwheel@v2.23.3 - # to supply options, put them in 'env', like: - # env: - # CIBW_SOME_OPTION: value env: CIBW_BUILD: ${{ matrix.cibuild }} CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }} From 50ccbff88bcc5e1d49c9c7971a3ddec5118e6cf9 Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 11:39:30 +0900 Subject: [PATCH 68/76] set arch to aarch64 --- .github/workflows/try_build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index efb3161dd..e4e2089d2 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -31,7 +31,7 @@ jobs: cibw_arch: "musllinux" - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner cibuild: "*manylinux*" - cibw_arch: "manylinux" + cibw_arch: "aarch64" steps: - name: Free Disk Space (Ubuntu) if: matrix.os == 'ubuntu-latest' @@ -72,7 +72,6 @@ jobs: libpng-devel CIBW_BUILD: ${{ matrix.cibuild }} CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }} - # … any other cibuildwheel env you already have … - name: Build wheels (all other platforms) if: matrix.os != 'ubuntu-24.04-arm' From 2917fd509894a76f22860e7f77f219808091dd9e Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 13:30:42 +0900 Subject: [PATCH 69/76] skipping before_build stage --- .github/workflows/try_build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index e4e2089d2..1d4076604 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -70,6 +70,7 @@ jobs: curl-devel \ expat-devel \ libpng-devel + CIBW_BEFORE_BUILD_LINUX: "true" CIBW_BUILD: ${{ matrix.cibuild }} CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }} From c816655fde5f717dbaf6dd24402d6ff56d6a011f Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 14:50:55 +0900 Subject: [PATCH 70/76] fixing if statement. porting changes back to original file --- .github/workflows/build.yml | 27 +++++++++++++++++++++------ .github/workflows/try_build.yml | 2 +- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ceaa6b5c9..d3b1fba45 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: cibw_arch: "musllinux" - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner cibuild: "*manylinux*" - cibw_arch: "manylinux" + cibw_arch: "aarch64" steps: - name: Free Disk Space (Ubuntu) if: matrix.os == 'ubuntu-latest' @@ -51,29 +51,44 @@ jobs: uses: styfle/cancel-workflow-action@0.12.1 - uses: actions/checkout@v4 - name: ccache + if: matrix.os != 'ubuntu-24.04-arm' uses: hendrikmuhs/ccache-action@v1.2 with: key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS max-size: "5G" - name: Install dependencies + if: matrix.os != 'ubuntu-24.04-arm' run: | env export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH HOST_CCACHE_DIR="$(ccache -k cache_dir)" mkdir -p $HOST_CCACHE_DIR - - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions + - name: Build wheels (ARM) + if: matrix.os == 'ubuntu-24.04-arm' + uses: pypa/cibuildwheel@v2.23.3 + env: + # override the default CentOS “yum install … ccache” and drop ccache + CIBW_BEFORE_ALL_LINUX: | + yum install -y \ + zlib-devel \ + curl-devel \ + expat-devel \ + libpng-devel + CIBW_BEFORE_BUILD_LINUX: "true" + CIBW_BUILD: ${{ matrix.cibuild }} + CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }} + + - name: Build wheels (all other platforms) + if: matrix.os != 'ubuntu-24.04-arm' uses: pypa/cibuildwheel@v2.23.3 - # to supply options, put them in 'env', like: - # env: - # CIBW_SOME_OPTION: value env: CIBW_BUILD: ${{ matrix.cibuild }} CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }} CIBW_DEPENDENCY_VERSIONS_MACOS: cibw_constraints.txt - name: Download Cache from Docker (linux only) - if: ${{ runner.os == 'Linux' }} + if: runner.os == 'Linux' && matrix.os != 'ubuntu-24.04-arm' # hack until https://github.com/pypa/cibuildwheel/issues/1030 is fixed run: | env diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index 1d4076604..d72a6cf7c 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -83,7 +83,7 @@ jobs: CIBW_DEPENDENCY_VERSIONS_MACOS: cibw_constraints.txt - name: Download Cache from Docker (linux only) - if: ${{ runner.os == 'Linux' }} and ${{ matrix.os != 'ubuntu-24.04-arm' }} + if: runner.os == 'Linux' && matrix.os != 'ubuntu-24.04-arm' # hack until https://github.com/pypa/cibuildwheel/issues/1030 is fixed run: | env From 0bf0b5210c4658ef437851e458b432e3bd80b77a Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 16:28:20 +0900 Subject: [PATCH 71/76] removing temporary test workflow --- .github/workflows/try_build.yml | 99 --------------------------------- 1 file changed, 99 deletions(-) delete mode 100644 .github/workflows/try_build.yml diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml deleted file mode 100644 index d72a6cf7c..000000000 --- a/.github/workflows/try_build.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Build Python Wheels -# https://docs.github.com/en/free-pro-team@latest/actions/guides/building-and-testing-python - -on: - push: - branches: - - support-arm-linux - - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - max-parallel: 12 - matrix: - include: - - os: "macos-13" # intel runner - cibuild: "*macosx*" - cibw_arch: "macos_x86_64" - macos-arch: "x86_64" - - os: "macos-14" # M1 runner - cibuild: "*macosx*" - cibw_arch: "macos_arm64" - macos-arch: "arm64" - - os: "ubuntu-latest" - cibuild: "*manylinux*" - cibw_arch: "manylinux" - - os: "ubuntu-latest" - cibuild: "*musllinux*" - cibw_arch: "musllinux" - - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner - cibuild: "*manylinux*" - cibw_arch: "aarch64" - steps: - - name: Free Disk Space (Ubuntu) - if: matrix.os == 'ubuntu-latest' - uses: jlumbroso/free-disk-space@main - with: - android: true - dotnet: true - haskell: true - large-packages: true - - uses: hmarr/debug-action@v3 - - name: Cancel Workflow Action - uses: styfle/cancel-workflow-action@0.12.1 - - uses: actions/checkout@v4 - - name: ccache - if: matrix.os != 'ubuntu-24.04-arm' - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS - max-size: "5G" - - name: Install dependencies - if: matrix.os != 'ubuntu-24.04-arm' - run: | - env - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH - HOST_CCACHE_DIR="$(ccache -k cache_dir)" - mkdir -p $HOST_CCACHE_DIR - - name: Build wheels (ARM) - if: matrix.os == 'ubuntu-24.04-arm' - uses: pypa/cibuildwheel@v2.23.3 - env: - # override the default CentOS “yum install … ccache” and drop ccache - CIBW_BEFORE_ALL_LINUX: | - yum install -y \ - zlib-devel \ - curl-devel \ - expat-devel \ - libpng-devel - CIBW_BEFORE_BUILD_LINUX: "true" - CIBW_BUILD: ${{ matrix.cibuild }} - CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }} - - - name: Build wheels (all other platforms) - if: matrix.os != 'ubuntu-24.04-arm' - uses: pypa/cibuildwheel@v2.23.3 - env: - CIBW_BUILD: ${{ matrix.cibuild }} - CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }} - CIBW_DEPENDENCY_VERSIONS_MACOS: cibw_constraints.txt - - - name: Download Cache from Docker (linux only) - if: runner.os == 'Linux' && matrix.os != 'ubuntu-24.04-arm' - # hack until https://github.com/pypa/cibuildwheel/issues/1030 is fixed - run: | - env - ccache -s - HOST_CCACHE_DIR="$(ccache -k cache_dir)" - rm -rf $HOST_CCACHE_DIR - mv ./wheelhouse/.ccache $HOST_CCACHE_DIR - ls -la $HOST_CCACHE_DIR - ccache -s - - uses: actions/upload-artifact@v4 - with: - name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} - path: ./wheelhouse/*.whl \ No newline at end of file From aba912107bff5722cb2fb3af5bf6fe133e836199 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 24 May 2025 00:09:52 +0200 Subject: [PATCH 72/76] Refined solution, so it would allow a larger coordinate range. --- src/db/db/dbEdgesUtils.cc | 8 +++++--- src/db/unit_tests/dbEdgesTests.cc | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index 382c378bd..68db12206 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -306,12 +306,14 @@ EdgeOrientationFilter::EdgeOrientationFilter (double a, bool inverse, bool absol bool EdgeOrientationFilter::selected (const db::Edge &edge, db::properties_id_type) const { + db::Vector en = db::Vector (std::max (edge.dx_abs (), edge.dy_abs ()), 0); + // NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded). // A horizontal edge has 0 degree, a vertical one has 90 degree. if (edge.dx () < 0 || (edge.dx () == 0 && edge.dy () < 0)) { - return m_checker (db::Vector (edge.ortho_length (), 0), -edge.d ()); + return m_checker (en, -edge.d ()); } else { - return m_checker (db::Vector (edge.ortho_length (), 0), edge.d ()); + return m_checker (en, edge.d ()); } } @@ -363,7 +365,7 @@ SpecialEdgeOrientationFilter::selected (const db::Edge &edge, properties_id_type } db::Vector en, ev; - en = db::Vector (edge.ortho_length (), 0); + en = db::Vector (std::max (edge.dx_abs (), edge.dy_abs ()), 0); // NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded). // A horizontal edge has 0 degree, a vertical one has 90 degree. diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index 662aceb7b..2c20bf043 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -265,6 +265,9 @@ TEST(4) // issue-2060 { db::EdgeOrientationFilter f1 (90.0, true, false); + db::EdgeOrientationFilter f2 (90.0, false, false); + db::EdgeOrientationFilter f45 (45.0, false, false); + db::SpecialEdgeOrientationFilter fs (db::SpecialEdgeOrientationFilter::Diagonal, false); db::Edges rr; rr.insert (db::Box (db::Point (0, 0), db::Point (1000, 4000000))); @@ -273,6 +276,21 @@ TEST(4) rr.clear (); rr.insert (db::Box (db::Point (0, 0), db::Point (1000, 400000))); EXPECT_EQ (db::compare (rr.filtered (f1), "(1000,0;0,0);(0,400000;1000,400000)"), true); + + rr.clear (); + rr.insert (db::Box (db::Point (0, -1000000000), db::Point (1000, 1000000000))); + EXPECT_EQ (db::compare (rr.filtered (f1), "(1000,-1000000000;0,-1000000000);(0,1000000000;1000,1000000000)"), true); + + rr.clear (); + rr.insert (db::Box (db::Point (0, -1000000000), db::Point (1000, 1000000000))); + EXPECT_EQ (db::compare (rr.filtered (f2), "(0,-1000000000;0,1000000000);(1000,1000000000;1000,-1000000000)"), true); + + EXPECT_EQ (f2.selected (db::Edge (db::Point (0, -1000000000), db::Point (0, 1000000000)), size_t (0)), true); + EXPECT_EQ (f2.selected (db::Edge (db::Point (0, -1000000000), db::Point (1, 1000000000)), size_t (0)), false); + EXPECT_EQ (f45.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000000)), size_t (0)), true); + EXPECT_EQ (f45.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000001)), size_t (0)), false); + EXPECT_EQ (fs.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000000)), size_t (0)), true); + EXPECT_EQ (fs.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000001)), size_t (0)), false); } } From 094e11897affec7a9e4a69676205c09c6e916a28 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 24 May 2025 22:33:18 +0200 Subject: [PATCH 73/76] Added the new feature: 'flag_missing_ports' --- src/db/db/dbLayoutVsSchematic.cc | 38 +++ src/db/db/dbLayoutVsSchematic.h | 16 + src/db/db/gsiDeclDbLayoutVsSchematic.cc | 12 + src/lvs/lvs/built-in-macros/_lvs_engine.rb | 9 +- src/lvs/lvs/built-in-macros/_lvs_netter.rb | 27 ++ src/lvs/unit_tests/lvsSimpleTests.cc | 5 + testdata/lvs/flag_missing_ports.cir | 23 ++ testdata/lvs/flag_missing_ports.gds | Bin 0 -> 4358 bytes testdata/lvs/flag_missing_ports.lvs | 76 +++++ testdata/lvs/flag_missing_ports.lvsdb | 362 +++++++++++++++++++++ testdata/lvs/flag_missing_ports.sch | 7 + 11 files changed, 574 insertions(+), 1 deletion(-) create mode 100644 testdata/lvs/flag_missing_ports.cir create mode 100644 testdata/lvs/flag_missing_ports.gds create mode 100644 testdata/lvs/flag_missing_ports.lvs create mode 100644 testdata/lvs/flag_missing_ports.lvsdb create mode 100644 testdata/lvs/flag_missing_ports.sch diff --git a/src/db/db/dbLayoutVsSchematic.cc b/src/db/db/dbLayoutVsSchematic.cc index 449f49d69..4104d67e9 100644 --- a/src/db/db/dbLayoutVsSchematic.cc +++ b/src/db/db/dbLayoutVsSchematic.cc @@ -25,6 +25,7 @@ #include "dbLayoutVsSchematic.h" #include "dbLayoutVsSchematicWriter.h" #include "dbLayoutVsSchematicReader.h" +#include "dbNetlistCompareUtils.h" namespace db { @@ -86,6 +87,43 @@ db::NetlistCrossReference *LayoutVsSchematic::make_cross_ref () return mp_cross_ref.get (); } +bool +LayoutVsSchematic::flag_missing_ports (const db::Circuit *circuit) +{ + if (! mp_cross_ref.get ()) { + return false; + } + + db::NetlistCrossReference::PerCircuitData *pcd = const_cast (mp_cross_ref->per_circuit_data_for (std::make_pair (circuit, circuit))); + if (! pcd) { + return false; + } + + bool error = false; + + for (auto n = pcd->nets.begin (); n != pcd->nets.end (); ++n) { + + const db::Net *schem = n->pair.second; + const db::Net *layout = n->pair.first; + + if (schem && layout && schem->begin_pins () != schem->end_pins ()) { + + if (db::name_compare (layout, schem) != 0) { + + std::string msg = tl::sprintf (tl::to_string (tr ("Port mismatch '%s' vs. '%s'")), layout->expanded_name (), schem->expanded_name ()); + db::LogEntryData entry (db::Error, msg); + + pcd->log_entries.push_back (entry); + + error = true; + + } + } + + } + + return !error; +} void LayoutVsSchematic::save (const std::string &path, bool short_format) { diff --git a/src/db/db/dbLayoutVsSchematic.h b/src/db/db/dbLayoutVsSchematic.h index 1e27ea954..6c2b242e8 100644 --- a/src/db/db/dbLayoutVsSchematic.h +++ b/src/db/db/dbLayoutVsSchematic.h @@ -144,6 +144,22 @@ public: */ db::NetlistCrossReference *make_cross_ref (); + /** + * @brief Checks top-level port names + * + * This method checks that every top-level pin has a corresponding + * schematic pin and their names are equivalent. This verifies that + * all pins are labelles properly. + * + * Errors are placed in the log file. The return values indicates + * if there are no errors. + * + * The circuit is either a schematic or layout circuit. + * + * See issue #2055. + */ + bool flag_missing_ports (const db::Circuit *circuit); + /** * @brief Saves the database to the given path * diff --git a/src/db/db/gsiDeclDbLayoutVsSchematic.cc b/src/db/db/gsiDeclDbLayoutVsSchematic.cc index f17694359..87e1d6346 100644 --- a/src/db/db/gsiDeclDbLayoutVsSchematic.cc +++ b/src/db/db/gsiDeclDbLayoutVsSchematic.cc @@ -110,6 +110,18 @@ Class decl_dbLayoutVsSchematic (decl_dbLayoutToNetlist, " "\n" "See \\NetlistCrossReference for more details.\n" ) + + gsi::method ("flag_missing_ports", &db::LayoutVsSchematic::flag_missing_ports, gsi::arg ("circuit"), + "@brief Flags inconsistent port labels in the given circuit\n" + "@param circuit Either a layout or schematic circuit\n" + "@return True, if no errors were found\n" + "This method will check all schematic nets which have pins and tests whether the corresponding layout net " + "has the same name. This way, it is checked if the pins are properly labelled.\n" + "\n" + "The method must be called after the compare step was successful. Error messages will be added " + "to the log entries. If an error occured or the cross reference is not value, 'false' is returned.\n" + "\n" + "This method was introduced in version 0.30.2." + ) + gsi::method_ext ("write_l2n", &save_l2n, gsi::arg ("path"), gsi::arg ("short_format", false), "@brief Writes the \\LayoutToNetlist part of the object to a file.\n" "This method employs the native format of KLayout.\n" diff --git a/src/lvs/lvs/built-in-macros/_lvs_engine.rb b/src/lvs/lvs/built-in-macros/_lvs_engine.rb index 2465c2ca7..11959cf40 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_engine.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_engine.rb @@ -225,9 +225,16 @@ module LVS # @synopsis lvs_data # See \Netter#lvs_data for a description of that function. + # %LVS% + # @name flag_missing_ports + # @brief Checks if all top level ports are properly labelled + # @synopsis flag_missing_ports + # See \Netter#flag_missing_ports for a description of that function. + %w(schematic compare split_gates join_symmetric_nets tolerance ignore_parameter enable_parameter disable_parameter blank_circuit align same_nets same_nets! same_circuits same_device_classes equivalent_pins - min_caps max_res max_depth max_branch_complexity consider_net_names lvs_data no_lvs_hints).each do |f| + min_caps max_res max_depth max_branch_complexity consider_net_names lvs_data no_lvs_hints + flag_missing_ports).each do |f| eval <<"CODE" def #{f}(*args) _netter.#{f}(*args) diff --git a/src/lvs/lvs/built-in-macros/_lvs_netter.rb b/src/lvs/lvs/built-in-macros/_lvs_netter.rb index 4b3a44f7c..c4f8f5197 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_netter.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_netter.rb @@ -530,6 +530,33 @@ CODE @comparer_config << lambda { |comparer| comparer.with_log = false } end + # %LVS% + # @name flag_missing_ports + # @brief Flags inconsistently labelled or missing ports in the current top circuit + # @synopsis flag_missing_ports + # This method must be called after "compare" was executed successfully and will + # report errors if pins in the current top circuit's schematic are not labelled + # correspondingly in the layout. This prevents swapping of port labels or + # pads. + # + # @code + # success = compare + # success && flag_missing_ports + # @/code + # + # Note that in order to use this method, the top circuit from the schematic netlist + # needs to have pins. This may not be always the case - for example, if the top + # level circuit is not a subcircuit in a Spice netlist. + + def flag_missing_ports + + lvs_data.netlist || raise("Netlist not extracted yet") + lvs_data.xref || raise("Compare step was not executed yet") + + lvs_data.flag_missing_ports(lvs_data.netlist.top_circuit) + + end + # %LVS% # @name same_nets # @brief Establishes an equivalence between the nets diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index dbf89d211..69fc7e1b7 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -357,3 +357,8 @@ TEST(62_LayerNames) run_test (_this, "layer_names", "layer_names.gds", false, true, "TOP"); } +TEST(63_FlagMissingPorts) +{ + run_test (_this, "flag_missing_ports", "flag_missing_ports.gds", false, true, "TOP"); +} + diff --git a/testdata/lvs/flag_missing_ports.cir b/testdata/lvs/flag_missing_ports.cir new file mode 100644 index 000000000..9a86f30d6 --- /dev/null +++ b/testdata/lvs/flag_missing_ports.cir @@ -0,0 +1,23 @@ +* Extracted by KLayout + +* cell ND2X1 +* pin VSS +* pin VDD +* pin b +* pin X +* pin SUBSTRATE +.SUBCKT ND2X1 1 3 5 6 7 +* net 1 VSS +* net 3 VDD +* net 5 b +* net 6 X +* net 7 SUBSTRATE +* device instance $1 r0 *1 0.85,5.8 PMOS +M$1 1 6 2 4 PMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +* device instance $2 r0 *1 1.55,5.8 PMOS +M$2 2 5 1 4 PMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +* device instance $3 r0 *1 0.85,2.135 NMOS +M$3 8 6 3 7 NMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U +* device instance $4 r0 *1 1.55,2.135 NMOS +M$4 2 5 8 7 NMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +.ENDS ND2X1 diff --git a/testdata/lvs/flag_missing_ports.gds b/testdata/lvs/flag_missing_ports.gds new file mode 100644 index 0000000000000000000000000000000000000000..9311b21cce8daa406eccb94742079d798362305c GIT binary patch literal 4358 zcmb`KO^6&t6vtn8PxpN6Cc8U3Y_jW!tK<;EfaC+zONg?F;${O~=lsK|og*FijqcnTuHlb-Y>9y}!?AqUTn|9@5eyQ>+x_d^?s-|(vb_1>#`Ro!ES zrRk21leBxsayxD-HfN8MsHp=qzkH7iIGq*mw_t?g_5AS|>^;sL|&ALHR zT)wpV+{M$@>6sNrQp{f*P0ehwm`$hCm(*Z+FK)7v)>$&?vpcc=pYzkHZ)VB&ag&{l z_125^(@!zKYw&$FZnBd#eZQtZK>S_XYo)d>UfMoWW*V;)GW!CN6eI?GZI*r_%I?^( zQW$?1*p4b^I##GCdtGmhm74$ND^5qaOQN-ieqV)x?T$Rdt9;XN9Sv zRAHj*^-7!dPv30uDdBI~!6^T_{{J{Bo$pZH$RkG#JSn2=V55J=$Z;ffj9ZkwuD8s} z^{<^Km@yYlnrE%g`QQ7bZEc*{IgLpMW`m*Lh4YIU@>(~WANxFJd-EU77Ix#_@OE(8 zWP+Gpi+jU~E_;{53Wb^ddG~&Cd(?a5nX#AU%)Oa6HdgXovpIMthc_|c?TWI4(eBi5 zhx8Y(C#lbVu1J08cly5bU!Lyb!4$jNTfo{S@7RcI$fx(dS^s&*1E{d_PNf~#pD*l) zQ)y@Rr&>PYRp8zcj+NKS=Ux>tUe)M3#;iLuJN~NY>PhgHnJ7Ehsvl8@c}BSNs4pq| z$#(t9me(J{E>SPa4mSEvKJf9Aj620#BI6JJt$O<5IQpetl)cS==L+(LUq|Gh#Y`tL7#|NS4l|B|vVw)>}^{;8w?lClTAKeMLq`1n?t*g2lQ?#oz> zuRQ18z|C?BcUIsKzmvalX9X_*Rf|fqPIQWKQ@MxV?0wu)qU>P2sx$hvpua!pMcKhd zJ-@yXpOLFCA`euCD0^G{$u+MhsY~CgNPXyU^w0ko97i(V{K<;4xA|w>!<#;Sj%WO$ z>|mpR#>e$02iOJ1FUk%&{XE>@-znCA1zxN;mhio`mf-jNeffp6?m1`f4a%>52{k_%V%Erba_2YG;etY{6`fS~OcOd$mpzitHk5pel$U1r+xZrD7 zjJo2rPbxjWls+ psd, "G" => pgate, "W" => nwell, + "tS" => psd, "tD" => psd, "tG" => poly, "tW" => nwell }) + +# NMOS transistor device extraction +extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk, + "tS" => nsd, "tD" => nsd, "tG" => poly, "tW" => bulk }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(psd, contact) +connect(nsd, contact) +connect(poly, contact) +connect(ntie, contact) +connect(nwell, ntie) +connect(ptie, contact) +connect(contact, metal1) +connect(metal1, via1) +connect(via1, metal2) + +# Global +connect_global(bulk, "SUBSTRATE") +connect_global(ptie, "SUBSTRATE") + +# Compare section + +netlist.simplify + +compare + +flag_missing_ports + diff --git a/testdata/lvs/flag_missing_ports.lvsdb b/testdata/lvs/flag_missing_ports.lvsdb new file mode 100644 index 000000000..34ba019af --- /dev/null +++ b/testdata/lvs/flag_missing_ports.lvsdb @@ -0,0 +1,362 @@ +#%lvsdb-klayout + +# Layout +layout( + top(ND2X1) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l3 '1/0') + layer(l4 '5/0') + layer(l8 '8/0') + layer(l11 '9/0') + layer(l12) + layer(l13) + layer(l7) + layer(l2) + layer(l9) + layer(l6) + layer(l10) + + # Mask layer connectivity + connect(l3 l3 l9) + connect(l4 l4 l8) + connect(l8 l4 l8 l11 l2 l9 l6 l10) + connect(l11 l8 l11 l12) + connect(l12 l11 l12 l13) + connect(l13 l12 l13) + connect(l7 l7) + connect(l2 l8 l2) + connect(l9 l3 l8 l9) + connect(l6 l8 l6) + connect(l10 l8 l10) + + # Global nets and connectivity + global(l7 SUBSTRATE) + global(l10 SUBSTRATE) + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$PMOS PMOS + terminal(S + rect(l2 (-550 -750) (425 1500)) + ) + terminal(G + rect(l4 (-125 -750) (250 1500)) + ) + terminal(D + rect(l2 (125 -750) (450 1500)) + ) + terminal(B + rect(l3 (-125 -750) (250 1500)) + ) + ) + device(D$PMOS$1 PMOS + terminal(S + rect(l2 (-575 -750) (450 1500)) + ) + terminal(G + rect(l4 (-125 -750) (250 1500)) + ) + terminal(D + rect(l2 (125 -750) (425 1500)) + ) + terminal(B + rect(l3 (-125 -750) (250 1500)) + ) + ) + device(D$NMOS NMOS + terminal(S + rect(l6 (-550 -475) (425 950)) + ) + terminal(G + rect(l4 (-125 -475) (250 950)) + ) + terminal(D + rect(l6 (125 -475) (450 950)) + ) + terminal(B + rect(l7 (-125 -475) (250 950)) + ) + ) + device(D$NMOS$1 NMOS + terminal(S + rect(l6 (-575 -475) (450 950)) + ) + terminal(G + rect(l4 (-125 -475) (250 950)) + ) + terminal(D + rect(l6 (125 -475) (425 950)) + ) + terminal(B + rect(l7 (-125 -475) (250 950)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(ND2X1 + + # Circuit boundary + rect((-100 400) (2600 7600)) + + # Nets with their geometries + net(1 name(VSS) + rect(l8 (1110 5160) (180 180)) + rect(l8 (-180 920) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l11 (-240 -790) (300 1700)) + rect(l11 (-1350 0) (2400 800)) + rect(l11 (-1150 -400) (0 0)) + rect(l2 (-275 -2150) (425 1500)) + rect(l2 (-400 -1500) (425 1500)) + ) + net(2 + rect(l8 (1810 1770) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l8 (-1580 3760) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l8 (1220 920) (180 180)) + rect(l8 (-180 -1280) (180 180)) + rect(l8 (-180 370) (180 180)) + polygon(l11 (-240 -4180) (0 1390) (490 0) (0 -300) (-190 0) (0 -1090)) + rect(l11 (-110 1390) (300 1400)) + polygon(l11 (-1890 0) (0 600) (300 0) (0 -300) (1590 0) (0 -300)) + rect(l11 (-1890 600) (300 1400)) + rect(l11 (1100 -1700) (300 300)) + rect(l11 (-300 0) (300 1400)) + rect(l2 (-1750 -1450) (425 1500)) + rect(l2 (950 -1500) (425 1500)) + rect(l6 (-425 -4890) (425 950)) + ) + net(3 name(VDD) + rect(l8 (410 1770) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l11 (-240 -1300) (300 1360)) + rect(l11 (-650 -2160) (2400 800)) + rect(l11 (-1150 -400) (0 0)) + rect(l6 (-950 860) (425 950)) + ) + net(4 + rect(l3 (-100 4500) (2600 3500)) + ) + net(5 name(b) + rect(l4 (1425 2860) (250 1940)) + rect(l4 (-345 -950) (300 300)) + rect(l4 (-205 650) (250 2000)) + rect(l4 (-250 -2000) (250 2000)) + rect(l4 (-250 -5390) (250 1450)) + rect(l8 (-285 1050) (180 180)) + rect(l11 (-70 -90) (0 0)) + rect(l11 (-170 -150) (300 300)) + ) + net(6 name(X) + rect(l4 (725 2860) (250 1940)) + rect(l4 (-325 -1850) (300 300)) + rect(l4 (-225 1550) (250 2000)) + rect(l4 (-250 -2000) (250 2000)) + rect(l4 (-250 -5390) (250 1450)) + rect(l8 (-265 150) (180 180)) + rect(l11 (-90 -90) (0 0)) + rect(l11 (-150 -150) (300 300)) + ) + net(7 name(SUBSTRATE)) + net(8 + rect(l6 (975 1660) (425 950)) + rect(l6 (-400 -950) (425 950)) + ) + + # Outgoing pins and their connections to nets + pin(1 name(VSS)) + pin(3 name(VDD)) + pin(5 name(b)) + pin(6 name(X)) + pin(7 name(SUBSTRATE)) + + # Devices and their connections + device(1 D$PMOS + location(850 5800) + param(L 0.25) + param(W 1.5) + param(AS 0.6375) + param(AD 0.3375) + param(PS 3.85) + param(PD 1.95) + terminal(S 2) + terminal(G 6) + terminal(D 1) + terminal(B 4) + ) + device(2 D$PMOS$1 + location(1550 5800) + param(L 0.25) + param(W 1.5) + param(AS 0.3375) + param(AD 0.6375) + param(PS 1.95) + param(PD 3.85) + terminal(S 1) + terminal(G 5) + terminal(D 2) + terminal(B 4) + ) + device(3 D$NMOS + location(850 2135) + param(L 0.25) + param(W 0.95) + param(AS 0.40375) + param(AD 0.21375) + param(PS 2.75) + param(PD 1.4) + terminal(S 3) + terminal(G 6) + terminal(D 8) + terminal(B 7) + ) + device(4 D$NMOS$1 + location(1550 2135) + param(L 0.25) + param(W 0.95) + param(AS 0.21375) + param(AD 0.40375) + param(PS 1.4) + param(PD 2.75) + terminal(S 8) + terminal(G 5) + terminal(D 2) + terminal(B 7) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(ND2X1 + + # Nets + net(1 name(VDD)) + net(2 name(OUT)) + net(3 name(VSS)) + net(4 name(NWELL)) + net(5 name(B)) + net(6 name(A)) + net(7 name(BULK)) + net(8 name('1')) + + # Outgoing pins and their connections to nets + pin(1 name(VDD)) + pin(2 name(OUT)) + pin(3 name(VSS)) + pin(4 name(NWELL)) + pin(5 name(B)) + pin(6 name(A)) + pin(7 name(BULK)) + + # Devices and their connections + device(1 PMOS + name($1) + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 1) + terminal(G 6) + terminal(D 2) + terminal(B 4) + ) + device(2 PMOS + name($2) + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 1) + terminal(G 5) + terminal(D 2) + terminal(B 4) + ) + device(3 NMOS + name($3) + param(L 0.25) + param(W 0.95) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 3) + terminal(G 6) + terminal(D 8) + terminal(B 7) + ) + device(4 NMOS + name($4) + param(L 0.25) + param(W 0.95) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 8) + terminal(G 5) + terminal(D 2) + terminal(B 7) + ) + + ) +) + +# Cross reference +xref( + circuit(ND2X1 ND2X1 match + log( + entry(error description('Port mismatch \'$4\' vs. \'NWELL\'')) + entry(error description('Port mismatch \'$2\' vs. \'OUT\'')) + entry(error description('Port mismatch \'SUBSTRATE\' vs. \'BULK\'')) + entry(error description('Port mismatch \'VDD\' vs. \'VSS\'')) + entry(error description('Port mismatch \'VSS\' vs. \'VDD\'')) + entry(error description('Port mismatch \'X\' vs. \'A\'')) + ) + xref( + net(8 8 match) + net(4 4 match) + net(2 2 match) + net(7 7 match) + net(3 3 match) + net(1 1 match) + net(6 6 match) + net(5 5 match) + pin(() 3 match) + pin(() 1 match) + pin(4 6 match) + pin(1 2 match) + pin(0 0 match) + pin(3 5 match) + pin(2 4 match) + device(3 3 match) + device(4 4 match) + device(1 1 match) + device(2 2 match) + ) + ) +) diff --git a/testdata/lvs/flag_missing_ports.sch b/testdata/lvs/flag_missing_ports.sch new file mode 100644 index 000000000..f0f917e0a --- /dev/null +++ b/testdata/lvs/flag_missing_ports.sch @@ -0,0 +1,7 @@ + +.SUBCKT ND2X1 VDD OUT VSS NWELL B A BULK +M$1 OUT A VDD NWELL PMOS L=0.25U W=1.5U +M$2 OUT B VDD NWELL PMOS L=0.25U W=1.5U +M$3 1 A VSS BULK NMOS L=0.25U W=0.95U +M$4 OUT B 1 BULK NMOS L=0.25U W=0.95U +.ENDS ND2X1 From 125e06bd49e120b4981adafcea84456ff58dc939 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 17:53:11 +0200 Subject: [PATCH 74/76] Added doc, added a warning about no pins found at top level. --- src/db/db/dbLayoutVsSchematic.cc | 12 +++++- src/doc/doc/about/lvs_ref_global.xml | 9 ++++ src/doc/doc/about/lvs_ref_netter.xml | 21 ++++++++++ src/doc/doc/manual/lvs_compare.xml | 63 ++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbLayoutVsSchematic.cc b/src/db/db/dbLayoutVsSchematic.cc index 4104d67e9..a31cfe333 100644 --- a/src/db/db/dbLayoutVsSchematic.cc +++ b/src/db/db/dbLayoutVsSchematic.cc @@ -100,6 +100,7 @@ LayoutVsSchematic::flag_missing_ports (const db::Circuit *circuit) } bool error = false; + bool any = false; for (auto n = pcd->nets.begin (); n != pcd->nets.end (); ++n) { @@ -108,11 +109,12 @@ LayoutVsSchematic::flag_missing_ports (const db::Circuit *circuit) if (schem && layout && schem->begin_pins () != schem->end_pins ()) { + any = true; + if (db::name_compare (layout, schem) != 0) { std::string msg = tl::sprintf (tl::to_string (tr ("Port mismatch '%s' vs. '%s'")), layout->expanded_name (), schem->expanded_name ()); db::LogEntryData entry (db::Error, msg); - pcd->log_entries.push_back (entry); error = true; @@ -122,6 +124,14 @@ LayoutVsSchematic::flag_missing_ports (const db::Circuit *circuit) } + if (! any) { + + std::string msg = tl::to_string (tr ("No pins found in circuit during 'flag_missing_ports'")); + db::LogEntryData entry (db::Warning, msg); + pcd->log_entries.push_back (entry); + + } + return !error; } diff --git a/src/doc/doc/about/lvs_ref_global.xml b/src/doc/doc/about/lvs_ref_global.xml index ac633b4b3..d1121526c 100644 --- a/src/doc/doc/about/lvs_ref_global.xml +++ b/src/doc/doc/about/lvs_ref_global.xml @@ -82,6 +82,15 @@ See Netter#enable_parameter

See Netter#equivalent_pins for a description of that function.

+

"flag_missing_ports" - Checks if all top level ports are properly labelled

+ +

Usage:

+
    +
  • flag_missing_ports
  • +
+

+See Netter#flag_missing_ports for a description of that function. +

"ignore_parameter" - Specifies whether to ignore a parameter from a given device class for the compare

Usage:

diff --git a/src/doc/doc/about/lvs_ref_netter.xml b/src/doc/doc/about/lvs_ref_netter.xml index 463eebd69..be3e6e538 100644 --- a/src/doc/doc/about/lvs_ref_netter.xml +++ b/src/doc/doc/about/lvs_ref_netter.xml @@ -186,6 +186,27 @@ case pin names for SPICE netlists.

Use this method andwhere in the script before the compare call.

+

"flag_missing_ports" - Flags inconsistently labelled or missing ports in the current top circuit

+ +

Usage:

+
    +
  • flag_missing_ports
  • +
+

+This method must be called after "compare" was executed successfully and will +report errors if pins in the current top circuit's schematic are not labelled +correspondingly in the layout. This prevents swapping of port labels or +pads. +

+

+success = compare
+success && flag_missing_ports
+
+

+Note that in order to use this method, the top circuit from the schematic netlist +needs to have pins. This may not be always the case - for example, if the top +level circuit is not a subcircuit in a Spice netlist. +

"ignore_parameter" - Skip a specific parameter for a given device class name during device compare

Usage:

diff --git a/src/doc/doc/manual/lvs_compare.xml b/src/doc/doc/manual/lvs_compare.xml index f58319310..d4fe34697 100644 --- a/src/doc/doc/manual/lvs_compare.xml +++ b/src/doc/doc/manual/lvs_compare.xml @@ -296,6 +296,69 @@ tolerance("NMOS", "L", :absolute => 0.05, :relative => 0.01)
min_caps(1e-16)
+

Checking pin labels

+ +

+ LVS is basically name-agnostic, so except for resolving ambiguities, net names are + not considered. Topology matching has priority - if nets are not labelled + properly, LVS by default does not care. +

+ +

+ This may have adverse effects in the case of outbound connections - for example + pads. It's a fatal error to connect the chip pads incorrectly. To mitigate this + issue, the "flag_missing_ports" function is provided. +

+ +

+ You need to call this function after the compare step, i.e. +

+ +
compare
+flag_missing_ports
+ +

+ Or, if you want to quench pseudo errors, only in case of successful compare: +

+ +
success = compare
+success && flag_missing_ports
+ +

+ This function takes the schematic top circuit and investigates all + nets that are connected to a pin. It will check the name (label) of the + corresponding layout net and if names do not match, an error is written + into the log section of the LVS report. +

+ +

+ When you use this feature while working yourself bottom-up in the design, + it will make sure that all pins are properly labelled. If you use pins + in the top level circuit to describe the chip pads, this feature will make + sure that the correct nets are connected to the pads with the corresponding labels + on them. +

+ +

+ Note that it is possible to have SPICE netlists which do not have pins + at the top level circuit - e.g. if the top level circuit is not a SUBCKT. + In that case, the function will not report errors as there are not pin-carrying + nets. Only a warning is issues saying that no top level pins have been found. +

+ +

+ You can use +

+ +
schematic.make_top_level_pins
+ +

+ to create pins if none are provided. However, this method will turn every net into a pin + and force you to label every net in the top circuit then. + Hence, it is better to provide pins inside the schematic netlist. + Also note, that "make_top_level_pins" is implicitly included in "schematic.simplify". +

+

Compare and netlist hierarchy

From 6cccb81293a4f26ff39c0ebfc9b5a6aabe1ff858 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 21:28:11 +0200 Subject: [PATCH 75/76] Implemented solution for #2057 (nanometer scalebar) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scale bar switches to nm below 0.1µm. Correspondingly it will switch to mm above 100µm. --- src/layview/layview/layGridNet.cc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/layview/layview/layGridNet.cc b/src/layview/layview/layGridNet.cc index b960579b1..92fc3b6ab 100644 --- a/src/layview/layview/layGridNet.cc +++ b/src/layview/layview/layGridNet.cc @@ -538,7 +538,22 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas) db::Point (xoffset + int (floor (0.5 + 2 * dgrid)), vp.height () - yoffset + rh / 2), ruler_color); - painter.draw_text (tl::sprintf ("%g \265m", grid * 2).c_str (), + double grid_value = grid * 2; + std::string fmt = "%g \265m"; + if (grid_value < 0.1 * (1 + db::epsilon)) { + grid_value *= 1000.0; + fmt = "%g nm"; + } else if (grid_value < 100.0 * (1 + db::epsilon)) { + fmt = "%g \265m"; + } else if (grid_value < 100000.0 * (1 + db::epsilon)) { + grid_value *= 1e-3; + fmt = "%g mm"; + } else { + grid_value *= 1e-6; + fmt = "%g m"; + } + + painter.draw_text (tl::sprintf (fmt, grid_value).c_str (), db::Point (xoffset + int (floor (0.5 + trans.ctrans (2 * grid))), vp.height () - yoffset - rh / 2 - 2), ruler_color, -1, 1); From 3a75665bfe9f91e211423ea15e4638e3d4474f38 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 21:50:45 +0200 Subject: [PATCH 76/76] Dropping setuptools requirements as this rules out older Python versions --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a436bf5c6..aee59ff0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools >= 77.0.3"] +requires = ["setuptools"] build-backend = "setuptools.build_meta" [tool.cibuildwheel]