From b32329c920523dcd2a246b190d2deeed7a4271ab Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Jun 2024 09:29:47 +0200 Subject: [PATCH 01/12] OASIS reader: avoiding slight rounding of DBU In python read/write cycle (discussion-2526) --- src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc | 4 +--- src/plugins/streamers/oasis/db_plugin/dbOASISReader.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index a5f63ce652..04e312470d 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -42,7 +42,6 @@ namespace db OASISReader::OASISReader (tl::InputStream &s) : m_stream (s), m_progress (tl::to_string (tr ("Reading OASIS file")), 10000), - m_dbu (0.001), m_expect_strict_mode (-1), mm_repetition (this, "repetition"), mm_placement_cell (this, "placement-cell"), @@ -648,8 +647,7 @@ OASISReader::do_read (db::Layout &layout) } // compute database unit in pixel per meter - m_dbu = 1.0e-6 / res; - layout.dbu (m_dbu * 1e6); + layout.dbu (1.0 / res); // read over table offsets if required bool table_offsets_at_end = get_uint (); diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h index 68a60f8443..6d381983be 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h @@ -124,7 +124,6 @@ class DB_PLUGIN_PUBLIC OASISReader tl::InputStream &m_stream; tl::AbsoluteProgress m_progress; std::string m_cellname; - double m_dbu; int m_expect_strict_mode; size_t m_first_cellname; size_t m_first_propname; From f4736759b63de15d8407bf7a5c7a9776c40efb4e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Jun 2024 09:55:46 +0200 Subject: [PATCH 02/12] issue #1742 fixed: proper tracking of references of RecursiveShapeIterator - this will fix the segfault, but not the general issue of the ticket. --- src/db/db/dbRecursiveShapeIterator.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 532fdd2477..96761a1aae 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -51,6 +51,7 @@ class RecursiveShapeReceiver; * shape classes and shape properties. */ class DB_PUBLIC RecursiveShapeIterator + : public gsi::ObjectBase { public: typedef db::Layout layout_type; From 62b1d26ce51508d8eb13d1b3b104506443dada62 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Jun 2024 10:30:22 +0200 Subject: [PATCH 03/12] Small bugfix: symlinks in the salt paths might have lead to macro duplication --- src/lay/lay/layMacroController.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lay/lay/layMacroController.cc b/src/lay/lay/layMacroController.cc index 727a90a1ad..0b11f32159 100644 --- a/src/lay/lay/layMacroController.cc +++ b/src/lay/lay/layMacroController.cc @@ -516,7 +516,7 @@ MacroController::sync_macro_sources () if (! t->base_path ().empty ()) { QDir base_dir (tl::to_qstring (t->base_path ())); if (base_dir.exists ()) { - std::string path = tl::to_string (base_dir.absolutePath ()); + std::string path = tl::to_string (base_dir.canonicalPath ()); tech_names_by_path [path].push_back (t->name ()); if (t->is_readonly ()) { readonly_paths.insert (path); @@ -559,7 +559,7 @@ MacroController::sync_macro_sources () description += " - " + tl::to_string (tr ("%1 branch").arg (tl::to_qstring (*f))); } - external_paths.push_back (ExternalPathDescriptor (tl::to_string (macro_dir.path ()), description, macro_categories () [c].name, lym::MacroCollection::TechFolder, readonly_paths.find (t->first) != readonly_paths.end ())); + external_paths.push_back (ExternalPathDescriptor (tl::to_string (macro_dir.canonicalPath ()), description, macro_categories () [c].name, lym::MacroCollection::TechFolder, readonly_paths.find (t->first) != readonly_paths.end ())); } @@ -591,7 +591,7 @@ MacroController::sync_macro_sources () if (*f != macro_categories () [c].name) { description += " - " + tl::to_string (tr ("%1 branch").arg (tl::to_qstring (*f))); } - external_paths.push_back (ExternalPathDescriptor (tl::to_string (macro_dir.path ()), description, macro_categories () [c].name, lym::MacroCollection::SaltFolder, g->is_readonly (), g->version ())); + external_paths.push_back (ExternalPathDescriptor (tl::to_string (macro_dir.canonicalPath ()), description, macro_categories () [c].name, lym::MacroCollection::SaltFolder, g->is_readonly (), g->version ())); } From 7082b0528d639e6bc1850d031b00ec05b25d8dd6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Jun 2024 16:53:01 +0200 Subject: [PATCH 04/12] [consider merging] Ruby binding bugfix: 'return' from block was behaving like 'break' The problem was that Ruby uses internal exceptions to implement 'return', 'break' and other types. These were mapped to a single tl::CancelException, rendering the effect of 'break' and 'return' the same. --- src/rba/rba/rba.cc | 32 +++++++++++++++++--------------- src/rba/rba/rbaInternal.h | 20 ++++++++++++++++++++ src/rba/rba/rbaUtils.cc | 23 ++++++++++------------- src/rba/rba/rbaUtils.h | 4 ++-- 4 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index e6ab2a357a..e51abfeb46 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -869,6 +869,12 @@ handle_exception (const std::string & /*where*/, rba::RubyError &ex) handle_exception (ex.exc (), ex.first_chance ()); } +static void +handle_exception (rba::RubyContinueException &ex) +{ + rb_jump_tag (ex.state ()); +} + static void handle_exception (const std::string &where, tl::Exception &ex) { @@ -897,6 +903,8 @@ handle_exception (const std::string &where) handle_exception ((where), ex); \ } catch (tl::ExitException &ex) { \ handle_exception ((where), ex); \ + } catch (rba::RubyContinueException &ex) { \ + handle_exception (ex); \ } catch (rba::RubyError &ex) { \ handle_exception ((where), ex); \ } catch (tl::Exception &ex) { \ @@ -1381,23 +1389,17 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor) std::unique_ptr iter ((gsi::IterAdaptorAbstractBase *) retlist.read (heap)); if (iter.get ()) { - try { - - gsi::SerialArgs rr (iter->serial_size ()); - while (! iter->at_end ()) { + gsi::SerialArgs rr (iter->serial_size ()); + while (! iter->at_end ()) { - rr.reset (); - iter->get (rr); + rr.reset (); + iter->get (rr); - VALUE value = pull_arg (meth->ret_type (), p, rr, heap); - rba_yield_checked (value); + VALUE value = pull_arg (meth->ret_type (), p, rr, heap); + rba_yield_checked (value); - iter->inc (); - - } + iter->inc (); - } catch (tl::CancelException &) { - // break encountered } } @@ -2402,7 +2404,7 @@ RubyInterpreter::require (const std::string &filename_utf8) RUBY_END_EXEC if (error) { - rba_check_error (); + rba_check_error (error); } } @@ -2423,7 +2425,7 @@ RubyInterpreter::load_file (const std::string &filename_utf8) RUBY_END_EXEC if (error) { - rba_check_error (); + rba_check_error (error); } } diff --git a/src/rba/rba/rbaInternal.h b/src/rba/rba/rbaInternal.h index 0a1ccd1acb..58a4fc7a80 100644 --- a/src/rba/rba/rbaInternal.h +++ b/src/rba/rba/rbaInternal.h @@ -65,6 +65,26 @@ class RubyError VALUE m_exc; }; +/** + * @brief A class responsible for forwarding exceptions raised from "break", "return" and other flow control functions + */ +class RubyContinueException + : public tl::CancelException +{ +public: + RubyContinueException (int state) + : tl::CancelException (), m_state (state) + { } + + int state () const + { + return m_state; + } + +private: + int m_state; +}; + /** * @brief The proxy object that represents the C++ object on the Ruby side */ diff --git a/src/rba/rba/rbaUtils.cc b/src/rba/rba/rbaUtils.cc index f11463965e..87f592593c 100644 --- a/src/rba/rba/rbaUtils.cc +++ b/src/rba/rba/rbaUtils.cc @@ -127,26 +127,23 @@ exceptions_blocked () } void -rba_check_error () +rba_check_error (int state) { VALUE lasterr = rb_errinfo (); - // NOTE: this seems to be required to avoid segfaults on Ruby 2.3.1 after - // a break was encountered. - rb_set_errinfo (Qnil); - - // Ruby employs this pseudo-exception to indicate a "break" of a loop + // Ruby employs this pseudo-exception to indicate a "break" or "return" of a loop. + // As this is an opaque condition, we continue Ruby execution later through a "RubyContinueException". #if HAVE_RUBY_VERSION_CODE < 10900 if (lasterr == Qnil) { - throw tl::CancelException (); + throw RubyContinueException (state); } #elif HAVE_RUBY_VERSION_CODE < 20300 if (TYPE (lasterr) == T_NODE) { - throw tl::CancelException (); + throw RubyContinueException (state); } #else if (TYPE (lasterr) == T_IMEMO) { - throw tl::CancelException (); + throw RubyContinueException (state); } #endif @@ -405,7 +402,7 @@ rba_class_new_instance_checked (int argc, VALUE *argv, VALUE klass) RUBY_END_EXEC if (error) { - rba_check_error (); + rba_check_error (error); } return ret; } @@ -458,7 +455,7 @@ VALUE rba_funcall2_checked (VALUE obj, ID id, int argc, VALUE *args) RUBY_END_EXEC if (error) { - rba_check_error (); + rba_check_error (error); } return ret; } @@ -497,7 +494,7 @@ rba_f_eval_checked (int argc, VALUE *argv, VALUE self) RUBY_END_EXEC if (error) { - rba_check_error (); + rba_check_error (error); } return ret; } @@ -513,7 +510,7 @@ rba_yield_checked (VALUE value) RUBY_END_EXEC if (error) { - rba_check_error (); + rba_check_error (error); } } diff --git a/src/rba/rba/rbaUtils.h b/src/rba/rba/rbaUtils.h index 1f9e6b5d86..6c06e20053 100644 --- a/src/rba/rba/rbaUtils.h +++ b/src/rba/rba/rbaUtils.h @@ -190,7 +190,7 @@ namespace rba tl::BacktraceElement rba_split_bt_information (const char *m, size_t l); void rba_get_backtrace_from_array (VALUE backtrace, std::vector &bt, unsigned int skip); -void rba_check_error (); +void rba_check_error (int state); VALUE rba_string_value_f (VALUE obj); VALUE rba_safe_string_value (VALUE obj); VALUE rba_safe_obj_as_string (VALUE obj); @@ -265,7 +265,7 @@ R rba_safe_func (R (*f) (A), A arg) RUBY_END_EXEC if (error) { - rba_check_error (); + rba_check_error (error); } return cp.r; } From 24a9e6824a0c617a477b57c3d96dbd1114c4bf9b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Jun 2024 16:54:20 +0200 Subject: [PATCH 05/12] Added tests for the last fix. --- src/gsi/gsi_test/gsiTest.cc | 12 ++--- testdata/ruby/basic_testcore.rb | 84 +++++++++++++++++++++++---------- 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/src/gsi/gsi_test/gsiTest.cc b/src/gsi/gsi_test/gsiTest.cc index 04b5dbaca7..bfc60b9fb5 100644 --- a/src/gsi/gsi_test/gsiTest.cc +++ b/src/gsi/gsi_test/gsiTest.cc @@ -1297,10 +1297,10 @@ static gsi::Class decl_b ("", "B", gsi::method ("an_cref", &B::an_cref) + // implemented by extension below: // gsi::iterator_ext ("b10", &b10b_ext, &b10e_ext) + - gsi::iterator ("b10_nc", &B::b10b_nc, &B::b10e_nc) + - gsi::iterator ("b11", &B::b11b, &B::b11e) + - gsi::iterator ("b12", &B::b12b, &B::b12e) + - gsi::iterator ("b13", &B::b13b, &B::b13e) + + gsi::iterator ("b10_nc|each_a_be_nc", &B::b10b_nc, &B::b10e_nc) + + gsi::iterator ("b11|each_a_be_v", &B::b11b, &B::b11e) + + gsi::iterator ("b12|each_a_be_p", &B::b12b, &B::b12e) + + gsi::iterator ("b13|each_a_be_cp", &B::b13b, &B::b13e) + gsi::method ("amember_or_nil_alt|amember_or_nil", &B::amember_or_nil) + gsi::method ("amember_ptr_alt|amember_ptr", &B::amember_ptr) + gsi::method ("xxx|amember_cptr", &B::amember_cptr) + @@ -1444,8 +1444,8 @@ static gsi::Class decl_b ("", "B", // extending B static gsi::ClassExt b_ext ( - gsi::iterator_ext ("b10", &b10b_ext, &b10e_ext) + - gsi::iterator_ext ("b10p", &b10bp_ext, &b10ep_ext) + gsi::iterator_ext ("b10|each_a_be", &b10b_ext, &b10e_ext) + + gsi::iterator_ext ("b10p|each_a_be_pp", &b10bp_ext, &b10ep_ext) ); CopyDetector *new_cd (int x) diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index 2cbfce073c..6183a77d43 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -665,7 +665,7 @@ def test_13 if !$leak_check begin - b.b10 { |a| arr.push(a.get_n) } # b10 is a const iterator - cannot call a1 on it + b.each_a_be { |a| arr.push(a.get_n) } # b10 is a const iterator - cannot call a1 on it rescue err_caught = true end @@ -679,7 +679,7 @@ def test_13 if !$leak_check begin - b.b10p { |a| arr.push(a.get_n) } # b10p is a const iterator - cannot call a1 on it + b.each_a_be_pp { |a| arr.push(a.get_n) } # b10p is a const iterator - cannot call a1 on it rescue err_caught = true end @@ -689,85 +689,85 @@ def test_13 end arr = [] - b.b10 { |a| arr.push(a.dup.get_n) } + b.each_a_be { |a| arr.push(a.dup.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.dup.b10 { |a| arr.push(a.dup.get_n) } + b.dup.each_a_be { |a| arr.push(a.dup.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b10 { |a| arr.push(a.get_n_const) } + b.each_a_be { |a| arr.push(a.get_n_const) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b10p { |a| arr.push(a.dup.get_n) } + b.each_a_be_pp { |a| arr.push(a.dup.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.dup.b10p { |a| arr.push(a.dup.get_n) } + b.dup.each_a_be_pp { |a| arr.push(a.dup.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b10p { |a| arr.push(a.get_n_const) } + b.each_a_be_pp { |a| arr.push(a.get_n_const) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b11 { |a| arr.push(a.get_n) } + b.each_a_be_v { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.dup.b11 { |a| arr.push(a.get_n) } + b.dup.each_a_be_v { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b12 { |a| arr.push(a.get_n) } + b.each_a_be_p { |a| arr.push(a.get_n) } assert_equal(arr, [7100, 7121, 7144, 7169]) arr = [] - b.dup.b12 { |a| arr.push(a.get_n) } + b.dup.each_a_be_p { |a| arr.push(a.get_n) } assert_equal(arr, [7100, 7121, 7144, 7169]) - aarr = b.b16a + aarr = b.av arr = [] aarr.each { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) - aarr = b.b16b + aarr = b.av_cref arr = [] aarr.each { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) - aarr = b.b16c + aarr = b.av_ref arr = [] aarr.each { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) - b.b17a( [ RBA::A.new_a( 101 ), RBA::A.new_a( -122 ) ] ) + b.av_cref = [ RBA::A.new_a( 101 ), RBA::A.new_a( -122 ) ] arr = [] - b.b11 { |a| arr.push(a.get_n) } + b.each_a_be_v { |a| arr.push(a.get_n) } assert_equal(arr, [101, -122]) - b.b17a( [] ) + b.av_cref = [] arr = [] - b.b11 { |a| arr.push(a.get_n) } + b.each_a_be_v { |a| arr.push(a.get_n) } assert_equal(arr, []) - b.b17b( [ RBA::A.new_a( 102 ), RBA::A.new_a( -123 ) ] ) + b.av_ref = [ RBA::A.new_a( 102 ), RBA::A.new_a( -123 ) ] arr = [] - b.b11 { |a| arr.push(a.get_n) } + b.each_a_be_v { |a| arr.push(a.get_n) } assert_equal(arr, [102, -123]) - b.b17c( [ RBA::A.new_a( 100 ), RBA::A.new_a( 121 ), RBA::A.new_a( 144 ) ] ) + b.av = [ RBA::A.new_a( 100 ), RBA::A.new_a( 121 ), RBA::A.new_a( 144 ) ] arr = [] - b.b11 { |a| arr.push(a.get_n) } + b.each_a_be_v { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) if !$leak_check arr = [] begin - b.b13 { |a| arr.push(a.get_n) } + b.each_a_be_cp { |a| arr.push(a.get_n) } rescue err_caught = true end @@ -777,11 +777,11 @@ def test_13 end arr = [] - b.b13 { |a| arr.push(a.get_n_const) } + b.each_a_be_cp { |a| arr.push(a.get_n_const) } assert_equal(arr, [-3100, -3121]) arr = [] - b.dup.b13 { |a| arr.push(a.get_n_const) } + b.dup.each_a_be_cp { |a| arr.push(a.get_n_const) } assert_equal(arr, [-3100, -3121]) arr = [] @@ -938,6 +938,38 @@ def test_13b end + def _iter_find(b, find) + b.each_a_be do |a| + if a.get_n_const == find + return true + end + end + return false + end + + # Iterator break, return, continue + def test_13c + + b = RBA::B.new + + arr = [] + b.each_a_be { |a| arr.push(a.get_n_const) } + assert_equal(arr, [100, 121, 144]) + + arr = [] + b.each_a_be do |a| + if a.get_n_const == 121 + next + end + arr.push(a.get_n_const) + end + assert_equal(arr, [100, 144]) + + assert_equal(_iter_find(b, 121), true) + assert_equal(_iter_find(b, 122), false) + + end + def test_14 a = RBA::A.new From 579da640ba066d69e8dab80cd18d3d10a48bc168 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Jun 2024 19:33:24 +0200 Subject: [PATCH 06/12] Enhancement: added optional layer/datatype information to DRC/LVS 'name' --- src/db/db/dbLayoutToNetlist.cc | 10 ++++-- src/db/db/dbLayoutToNetlist.h | 2 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 9 +++-- src/drc/drc/built-in-macros/_drc_netter.rb | 41 +++++++++++++++++++--- src/drc/unit_tests/drcSimpleTests.cc | 32 +++++++++++++++++ testdata/drc/drcSimpleTests_121.drc | 4 +-- 6 files changed, 86 insertions(+), 12 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 0fc0f40ada..924b11a044 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -1008,7 +1008,7 @@ std::string LayoutToNetlist::name (const ShapeCollection &coll) const } } -void LayoutToNetlist::register_layer (const ShapeCollection &collection, const std::string &n_in) +unsigned int LayoutToNetlist::register_layer (const ShapeCollection &collection, const std::string &n_in) { if (m_region_by_original.find (tl::id_of (collection.get_delegate ())) != m_region_by_original.end ()) { throw tl::Exception (tl::to_string (tr ("The layer is already registered"))); @@ -1042,10 +1042,14 @@ void LayoutToNetlist::register_layer (const ShapeCollection &collection, const s } + unsigned int layer = dl.layer (); + m_region_by_original [tl::id_of (collection.get_delegate ())] = dl; - m_region_of_layer [dl.layer ()] = dl; + m_region_of_layer [layer] = dl; m_named_regions [n] = dl; - m_name_of_layer [dl.layer ()] = n; + m_name_of_layer [layer] = n; + + return layer; } db::DeepLayer LayoutToNetlist::deep_layer_of (const db::ShapeCollection &coll) const diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 4d1d1ef494..04de1b4a02 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -317,7 +317,7 @@ class DB_PUBLIC LayoutToNetlist * In addition to regions, text collections can be registered too. * Including texts in "connect" makes net names begin assigned from the text strings. */ - void register_layer (const ShapeCollection &collection, const std::string &name = std::string ()); + unsigned int register_layer (const ShapeCollection &collection, const std::string &name = std::string ()); /** * @brief Gets the name of the given collection diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 3d11ee518e..8517787ba5 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -330,11 +330,16 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (const db::ShapeCollection ®ion) const) &db::LayoutToNetlist::name, gsi::arg ("l"), "@brief Gets the name of the given layer\n" ) + + gsi::method ("layer_index", (unsigned int (db::LayoutToNetlist::*) (const db::ShapeCollection ®ion) const) &db::LayoutToNetlist::layer_of, gsi::arg ("l"), + "@brief Gets the layer index for the given data object\n" + "This method has been introduced in version 0.29.3.\n" + ) + gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (unsigned int) const) &db::LayoutToNetlist::name, gsi::arg ("l"), "@brief Gets the name of the given layer (by index)\n" ) + - gsi::method ("register", (void (db::LayoutToNetlist::*) (const db::ShapeCollection &collection, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n", std::string ()), + gsi::method ("register", (unsigned int (db::LayoutToNetlist::*) (const db::ShapeCollection &collection, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n", std::string ()), "@brief Names the given layer\n" + "@return The index of the layer registered\n" "'l' must be a \\Region or \\Texts object.\n" "Flat regions or text collections must be registered with this function, before they can be used in \\connect. " "Registering will copy the shapes into the LayoutToNetlist object in this step to enable " @@ -346,7 +351,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "If required, the system will assign a name automatically." "\n" - "This method has been generalized in version 0.27.\n" + "This method has been generalized in version 0.27. Starting with version 0.29.3, the index of the layer is returned.\n" ) + gsi::method_ext ("layer_names", &l2n_layer_names, "@brief Returns a list of names of the layers kept inside the LayoutToNetlist object." diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 3e12ac8386..dd2fe9c9ab 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -299,6 +299,8 @@ def extract_devices(devex, layer_selection) # @name name # @brief Assigns a name to a layer # @synopsis name(layer, name) + # @synopsis name(layer, name, layer_number, datatype_number) + # @synopsis name(layer, name, layer_info) # Layer names are listed in the LayoutToNetlist (L2N) or LVS database. They # are used to identify the layers, the net or device terminal geometries are # on. It is usual to have computed layers, so it is necessary to indicate the @@ -325,6 +327,12 @@ def extract_devices(devex, layer_selection) # # \name can only be used once on a layer and the layer names must be # unique (not taken by another layer). + # + # The layer/datatype or LayerInfo specification is optional and will + # be used to configure the internal layout. This information is also + # persisted inside database files. Specifying a layer/datatype information + # is useful, if a layer is not an original layer, but is to be restored + # to an actual layout layer later. # %DRC% # @name name_prefix @@ -332,13 +340,33 @@ def extract_devices(devex, layer_selection) # @synopsis name_prefix(prefix) # See \\name for details. The default prefix is "l". - def name(l, name) + def name(*args) @engine._context("name") do + if args.size < 2 || args.size > 4 + raise("Two to four arguments expected (layer, name, [layer, datatype / layer properties])") + end + + l = args[0] l.is_a?(DRC::DRCLayer) || raise("First argument must be a layer") + + name = args[1] (name.is_a?(String) && name != "") || raise("Second argument must be a non-empty string") + lp = args[2] + if lp + if !lp.is_a?(RBA::LayerInfo) + lnum = args[2] + dnum = args[3] || 0 + lnum.is_a?(1.class) || raise("Layer argument needs to be a RBA::LayerInfo object or a layer number") + dnum.is_a?(1.class) || raise("Datatype argument needs to be an integer") + lp = RBA::LayerInfo::new(lnum, dnum) + elsif args[3] + raise("Three arguments are enough with RBA::LayerInfo") + end + end + id = l.data.data_id if @layers && @layers[id] @@ -349,7 +377,7 @@ def name(l, name) return end - self._register_layer(l.data, "name", name) + self._register_layer(l.data, "name", name, lp) end @@ -828,7 +856,7 @@ def _make_soft_connection_diodes(f) protected - def _register_layer(data, context, name = nil) + def _register_layer(data, context, name = nil, lp = nil) id = data.data_id ensure_data @@ -846,7 +874,12 @@ def _register_layer(data, context, name = nil) @layers[id] = [ data, name, context ] # every layer gets registered and intra-layer connections are made - @l2n.register(data, name) + index = @l2n.register(data, name) + + # set the layer properties if requested + if lp + @l2n.internal_layout.set_info(index, lp) + end end diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 6cc8d2fb52..b6a4da348d 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1911,3 +1911,35 @@ TEST(121_ShapesOfTerminal) db::compare_layouts (_this, layout, au, db::NoNormalization); } + +TEST(122_NamedLayers) +{ + std::string rs = tl::testdata (); + rs += "/drc/drcSimpleTests_122.drc"; + + std::string input = tl::testdata (); + input += "/drc/drcSimpleTests_122.gds"; + + std::string au_output = tl::testdata (); + au_output += "/drc/drcSimpleTests_au122.l2n"; + + std::string output = this->tmp_file ("tmp.l2n"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + compare_text_files (output, au_output); +} diff --git a/testdata/drc/drcSimpleTests_121.drc b/testdata/drc/drcSimpleTests_121.drc index 8f043e9269..13f53ea754 100644 --- a/testdata/drc/drcSimpleTests_121.drc +++ b/testdata/drc/drcSimpleTests_121.drc @@ -19,9 +19,9 @@ gate = active & poly name(l1, "l1") name(l2, "l2") name(l3, "l3") -name(sd, "sd") +name(sd, "sd", 17, 0) name(poly, "poly") -name(gate, "gate") +name(gate, "gate", RBA::LayerInfo::new(18, 0)) name(contact, "contact") mos_ex = RBA::DeviceExtractorMOS3Transistor::new("MOS") From 214e1736f0ed2ce613a75990caa98b521cbd726d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Jun 2024 21:40:28 +0200 Subject: [PATCH 07/12] Reading waiver comments on RVE database, automatically read associated .waived RVE DB file --- src/rdb/rdb/rdbRVEReader.cc | 35 +++++++++++++++++++++++-- src/rdb/unit_tests/rdbRVEReaderTests.cc | 10 +++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/rdb/rdb/rdbRVEReader.cc b/src/rdb/rdb/rdbRVEReader.cc index 96ddd466be..e29b48f1b3 100644 --- a/src/rdb/rdb/rdbRVEReader.cc +++ b/src/rdb/rdb/rdbRVEReader.cc @@ -62,10 +62,32 @@ class RVEReader virtual void read (Database &db) { try { + // TODO: do not allow waivers here? + // (otherwise, description lines like "WE0 ..." would be read as waivers) do_read (db); } catch (tl::Exception &ex) { error (ex.msg ()); } + + try { + + // try to find a waiver database and apply it if possible + std::string waived_source = m_input_stream.source () + ".waived"; + + tl::InputStream is_waived (waived_source); + + try { + Database db_waived; + RVEReader reader_waived (is_waived); + reader_waived.do_read (db_waived); + db.apply (db_waived); + } catch (tl::Exception &ex) { + warn (ex.msg ()); + } + + } catch (...) { + // ignore stream errors + } } void do_read (Database &db) @@ -157,7 +179,16 @@ class RVEReader while (*cp && isspace (*cp)) { ++cp; } - waivers.insert (std::make_pair (n, std::string ())).first->second = cp; + + auto wi = waivers.insert (std::make_pair (n, std::string ())); + // NOTE: first line is skipped (author, time) + if (! wi.second) { + std::string &s = wi.first->second; + if (! s.empty ()) { + s += "\n"; + } + s += cp; + } } else { @@ -176,7 +207,6 @@ class RVEReader std::map::const_iterator w = waivers.find (shape); bool waived = (w != waivers.end ()); - // TODO: add waiver string somehow ... if (at_end ()) { warn (tl::to_string (tr ("Unexpected end of file before the specified number of shapes was read - stopping."))); @@ -406,6 +436,7 @@ class RVEReader Item *item = db.create_item (cell->id (), cath->id ()); if (waived) { db.add_item_tag (item, waived_tag_id); + item->set_comment (w->second); } item->values ().swap (values); diff --git a/src/rdb/unit_tests/rdbRVEReaderTests.cc b/src/rdb/unit_tests/rdbRVEReaderTests.cc index 66368ae81d..94cc1c001f 100644 --- a/src/rdb/unit_tests/rdbRVEReaderTests.cc +++ b/src/rdb/unit_tests/rdbRVEReaderTests.cc @@ -81,3 +81,13 @@ TEST(3) { run_rve_test (_this, "rve3.db", "rve3_au_2.txt"); } + +TEST(4) +{ + run_rve_test (_this, "rve4.db", "rve4_au.txt"); +} + +TEST(5) +{ + run_rve_test (_this, "rve5.db", "rve5_au.txt"); +} From f721be7e32f19d95a5f98be6adfd7735f44d3aaa Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 17 Jun 2024 21:45:55 +0200 Subject: [PATCH 08/12] Trying to improve deep-mode XOR and NOT performance by introducing a local sweep that eliminates shapes also found in second input. --- src/buddies/src/bd/strmxor.cc | 3 + src/db/db/dbDeepRegion.cc | 120 +++++++++++++++++++++++++++++++--- src/db/db/dbDeepRegion.h | 3 +- src/db/db/dbHierProcessor.cc | 7 +- 4 files changed, 122 insertions(+), 11 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index de1f265e4e..33d8e6ca35 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -758,6 +758,9 @@ bool run_deep_xor (const XORData &xor_data) { db::DeepShapeStore dss; dss.set_threads (xor_data.threads); + + // 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 double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ()); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index d52df7f9c3..27a6f97b24 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -407,7 +407,7 @@ DeepRegion::begin_iter () const } else { const db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); - db::RecursiveShapeIterator iter (deep_layer ().layout (), top_cell, deep_layer ().layer ()); + db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ()); return std::make_pair (iter, db::ICplxTrans ()); } @@ -803,7 +803,7 @@ DeepRegion::and_with (const Region &other, PropertyConstraint property_constrain } else { - return new DeepRegion (and_or_not_with (other_deep, true, property_constraint)); + return new DeepRegion (and_with_impl (other_deep, property_constraint)); } } @@ -827,7 +827,7 @@ DeepRegion::not_with (const Region &other, PropertyConstraint property_constrain } else { - return new DeepRegion (and_or_not_with (other_deep, false, property_constraint)); + return new DeepRegion (not_with_impl (other_deep, property_constraint)); } } @@ -875,13 +875,13 @@ DeepRegion::andnot_with (const Region &other, PropertyConstraint property_constr } DeepLayer -DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op, db::PropertyConstraint property_constraint) const +DeepRegion::and_with_impl (const DeepRegion *other, db::PropertyConstraint property_constraint) const { DeepLayer dl_out (deep_layer ().derived ()); if (pc_skip (property_constraint)) { - db::BoolAndOrNotLocalOperation op (and_op); + db::BoolAndOrNotLocalOperation op (true); db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); configure_proc (proc); @@ -893,7 +893,7 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op, db::PropertyC } else { - db::BoolAndOrNotLocalOperationWithProperties op (and_op, &dl_out.layout ().properties_repository (), &deep_layer ().layout ().properties_repository (), &other->deep_layer ().layout ().properties_repository (), property_constraint); + db::BoolAndOrNotLocalOperationWithProperties op (true, &dl_out.layout ().properties_repository (), &deep_layer ().layout ().properties_repository (), &other->deep_layer ().layout ().properties_repository (), property_constraint); db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); configure_proc (proc); @@ -908,6 +908,110 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op, db::PropertyC return dl_out; } +DeepLayer +DeepRegion::not_with_impl (const DeepRegion *other, db::PropertyConstraint property_constraint) const +{ + DeepLayer dl_prep; + + // first run a local-only task, which is trivial if the layouts are the same + // TODO: consider property constraints too. + + if (deep_layer ().layout_index () == other->deep_layer ().layout_index () && pc_skip (property_constraint)) { + + dl_prep = deep_layer ().derived (); + + unsigned int layer_this = deep_layer ().layer (); + unsigned int layer_other = other->deep_layer ().layer (); + unsigned int layer_out = dl_prep.layer (); + + db::Layout &layout = dl_prep.layout (); + + bool any = false; + + if (layer_this != layer_other) { + + std::set called; + deep_layer ().initial_cell ().collect_called_cells (called); + called.insert (deep_layer ().initial_cell ().cell_index ()); + + for (auto ci = called.begin (); ci != called.end (); ++ci) { + + db::Cell &cell = layout.cell (*ci); + db::Shapes &shapes_out = cell.shapes (layer_out); + const db::Shapes &shapes_this = cell.shapes (layer_this); + const db::Shapes &shapes_other = cell.shapes (layer_other); + + db::Box overlap = (shapes_this.bbox () & shapes_other.bbox ()); + + if (! overlap.empty ()) { + + std::set others; + for (auto si = shapes_other.begin (db::ShapeIterator::Polygons); ! si.at_end (); ++si) { + if (si->type () == db::Shape::PolygonRef) { + others.insert (si->polygon_ref ()); + } + } + + for (auto si = shapes_this.begin (db::ShapeIterator::Polygons); ! si.at_end (); ++si) { + if (si->type () == db::Shape::PolygonRef && others.find (si->polygon_ref ()) != others.end ()) { + // drop duplicate + } else { + any = true; + shapes_out.insert (*si); + } + } + + } else if (! shapes_this.empty ()) { + + any = true; + shapes_out = shapes_this; + + } + + } + + } + + // stop if empty + if (! any) { + return dl_prep; + } + + } else { + dl_prep = deep_layer (); + } + + DeepLayer dl_out (deep_layer ().derived ()); + + if (pc_skip (property_constraint)) { + + db::BoolAndOrNotLocalOperation op (false); + + db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); + configure_proc (proc); + proc.set_threads (deep_layer ().store ()->threads ()); + proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ()); + proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ()); + + proc.run (&op, dl_prep.layer (), other->deep_layer ().layer (), dl_out.layer ()); + + } else { + + db::BoolAndOrNotLocalOperationWithProperties op (false, &dl_out.layout ().properties_repository (), &deep_layer ().layout ().properties_repository (), &other->deep_layer ().layout ().properties_repository (), property_constraint); + + db::local_processor proc (const_cast (&deep_layer ().layout ()), const_cast (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); + configure_proc (proc); + proc.set_threads (deep_layer ().store ()->threads ()); + proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ()); + proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ()); + + proc.run (&op, dl_prep.layer (), other->deep_layer ().layer (), dl_out.layer ()); + + } + + return dl_out; +} + std::pair DeepRegion::and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const { @@ -1008,8 +1112,8 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const other_deep_mapped->disable_progress (); } - DeepLayer n1 (and_or_not_with (other_deep_mapped.get (), false, property_constraint)); - DeepLayer n2 (other_deep_mapped->and_or_not_with (this, false, property_constraint)); + DeepLayer n1 (not_with_impl (other_deep_mapped.get (), property_constraint)); + DeepLayer n2 (other_deep_mapped->not_with_impl (this, property_constraint)); n1.add_from (n2); return new DeepRegion (n1); diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 1d55375540..6ddda9e14e 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -178,7 +178,8 @@ class DB_PUBLIC DeepRegion void init (); void ensure_merged_polygons_valid () const; - DeepLayer and_or_not_with(const DeepRegion *other, bool and_op, PropertyConstraint property_constraint) const; + DeepLayer not_with_impl (const DeepRegion *other, PropertyConstraint property_constraint) const; + DeepLayer and_with_impl (const DeepRegion *other, PropertyConstraint property_constraint) const; std::pair and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const; DeepRegion *apply_filter (const PolygonFilterBase &filter) const; diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index b2b5ae467c..c5d123d7c5 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -1737,12 +1737,14 @@ void local_processor::compute_contexts (local_processor_contexts intruder_shapes; if (intruder_cell) { + for (std::vector::const_iterator l = contexts.intruder_layers ().begin (); l != contexts.intruder_layers ().end (); ++l) { const db::Shapes *s = &intruder_cell->shapes (contexts.actual_intruder_layer (*l)); if (! s->empty ()) { intruder_shapes.insert (std::make_pair (*l, s)); } } + } db::box_convert inst_bcs (*mp_subject_layout, contexts.subject_layer ()); @@ -1824,8 +1826,8 @@ void local_processor::compute_contexts (local_processor_contexts scanner; db::addressable_object_from_shape heap; interaction_registration_inst2shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); @@ -1849,6 +1851,7 @@ void local_processor::compute_contexts (local_processor_contexts ()); + } // produce the tasks for computing the next-level interactions From f7f25879f06d7e827ea297a85fb47f643e879748 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 17 Jun 2024 21:55:35 +0200 Subject: [PATCH 09/12] Preserving layer indexes to preserve LVS DB format (note to myself: finally create a real compare tool for the latter) --- src/db/db/dbDeepRegion.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 27a6f97b24..8001acb737 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -911,6 +911,7 @@ DeepRegion::and_with_impl (const DeepRegion *other, db::PropertyConstraint prope DeepLayer DeepRegion::not_with_impl (const DeepRegion *other, db::PropertyConstraint property_constraint) const { + DeepLayer dl_out (deep_layer ().derived ()); DeepLayer dl_prep; // first run a local-only task, which is trivial if the layouts are the same @@ -981,8 +982,6 @@ DeepRegion::not_with_impl (const DeepRegion *other, db::PropertyConstraint prope dl_prep = deep_layer (); } - DeepLayer dl_out (deep_layer ().derived ()); - if (pc_skip (property_constraint)) { db::BoolAndOrNotLocalOperation op (false); From ef1192c6a884d19a1e9569321d92895f1bcc1c8a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 17 Jun 2024 23:12:46 +0200 Subject: [PATCH 10/12] Added missing files --- testdata/drc/drcSimpleTests_122.drc | 36 ++++++++ testdata/drc/drcSimpleTests_122.gds | Bin 0 -> 1990 bytes testdata/drc/drcSimpleTests_au122.l2n | 113 ++++++++++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 testdata/drc/drcSimpleTests_122.drc create mode 100644 testdata/drc/drcSimpleTests_122.gds create mode 100644 testdata/drc/drcSimpleTests_au122.l2n diff --git a/testdata/drc/drcSimpleTests_122.drc b/testdata/drc/drcSimpleTests_122.drc new file mode 100644 index 0000000000..ec6b68fde7 --- /dev/null +++ b/testdata/drc/drcSimpleTests_122.drc @@ -0,0 +1,36 @@ + +source $drc_test_source +report_netlist $drc_test_target + +deep + +l1 = input(1, 0) +l2 = input(2, 0) +l3 = input(3, 0) + +active = input(10, 0) +poly = input(11, 0) +contact = input(12, 0) + +sd = active - poly +gate = active & poly + +name(l1, "l1") +name(l2, "l2") +name(l3, "l3", RBA::LayerInfo::new(3, 0, "l3")) +name(sd, "sd", 17, 0) +name(poly, "poly", 11) +name(gate, "gate") +name(contact, "contact") + +mos_ex = RBA::DeviceExtractorMOS3Transistor::new("MOS") +extract_devices(mos_ex, { "SD" => sd, "G" => gate, "P" => poly }) + +connect(contact, poly) +connect(contact, sd) +connect(l1, contact) +connect(l1, l2) +connect(l2, l3) + +netlist + diff --git a/testdata/drc/drcSimpleTests_122.gds b/testdata/drc/drcSimpleTests_122.gds new file mode 100644 index 0000000000000000000000000000000000000000..c70c6c2a8be656f0d2509b2aa2b3cd2c95fc1b02 GIT binary patch literal 1990 zcmdUwziSjx5XWctcHeF8Hr|<>9??T1T0}T7ohU(aA&oykixd)4SXzW2!6JpFg@T1e z3PFo21T8EqEK-Q2m4!(Dh!i4e5>_R7erIQgeH-$U+Fh2ODtOclNh@>+^!7NiMGZfO9$K!Mkq1h( z=k(~2_s=AUneA`bnvW$p$7S5-A36MksI?*-TatAs)!te;T0e=};dw585xr9FlgoZR z_c3{jTAWs@ohkYh)1!}K|4wrsHQj&`N;#+aYHpC3$os9+Geh^RMn4cu;7jJ&&)BDj zCDjhPdtK3+w~|}T{YQLOB{}zs@mxP5vDOOz<^o5Yv=D1;OX|SfLZ)5E?&B{YM43IU ztQ6{lD{akSnpeD1{Eys7o$ib%k&*G5kh|&Ld+9bzwa05dJ-XRT{!Yl<95LsAD5lzv m($^EBQ%|4)yPJ~LO0|QY_(jJj#QJkZRVdXC=6dvuD58JdPQSAN literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au122.l2n b/testdata/drc/drcSimpleTests_au122.l2n new file mode 100644 index 0000000000..d34f94a3f9 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au122.l2n @@ -0,0 +1,113 @@ +#%l2n-klayout +W(TOP) +U(0.001) +L(l1 '1/0') +L(l2 '2/0') +L(l3 'l3 (3/0)') +L(poly '11/0') +L(contact '12/0') +L(sd '17/0') +C(l1 l1 l2 contact) +C(l2 l1 l2 l3) +C(l3 l2 l3) +C(poly poly contact) +C(contact l1 poly contact sd) +C(sd contact sd) +K(MOS MOS3) +D(D$MOS MOS + T(S + R(sd (-2000 -2600) (4000 800)) + ) + T(G + R(poly (-2000 -1800) (4000 3600)) + ) + T(D + R(sd (-2000 1800) (4000 800)) + ) +) +X(A + R((-2800 -2500) (5600 5200)) + N(1 + R(contact (-1900 2000) (600 600)) + R(contact (-600 -600) (600 600)) + R(contact (2600 -600) (600 600)) + R(contact (-600 -600) (600 600)) + R(contact (-2200 -600) (600 600)) + R(contact (-600 -600) (600 600)) + R(sd (-2300 -700) (4000 800)) + ) + N(2 + R(contact (-1900 -2400) (600 600)) + R(contact (2600 -600) (600 600)) + R(contact (-2200 -600) (600 600)) + R(sd (-2300 -700) (4000 800)) + ) + N(3 + R(poly (-2800 -1700) (5600 3600)) + ) + P(1) + P(2) + P(3) + D(1 D$MOS + Y(0 100) + E(L 3.6) + E(W 4) + E(AS 3.2) + E(AD 3.2) + E(PS 9.6) + E(PD 9.6) + T(S 2) + T(G 3) + T(D 1) + ) +) +X(AA + R((-2050 -1950) (5600 5200)) + N(1) + N(2) + N(3) + P(1) + P(2) + P(3) + X(1 A O(180) Y(750 750) + P(0 2) + P(1 1) + P(2 3) + ) +) +X(TOP + R((-3150 -3500) (8050 12650)) + N(1 I(B) + R(l1 (1650 5200) (3250 2800)) + R(l1 (-3300 -10700) (800 4000)) + R(l2 (1100 5100) (700 700)) + R(l2 (-2500 -8200) (700 700)) + Q(l3 (-790 -810) (0 1000) (1790 0) (0 7510) (1000 0) (0 -8510)) + R(l3 (-500 3410) (0 0)) + R(contact (-2100 3300) (600 600)) + R(contact (-600 1000) (600 600)) + ) + N(2 I(A) + R(l1 (-1400 1300) (2600 3000)) + R(l1 (-3550 -350) (4000 750)) + R(l1 (-1650 -1700) (0 0)) + R(contact (-1200 -1600) (600 600)) + R(contact (1000 -600) (600 600)) + ) + N(3 + R(l1 (-2350 8300) (4000 850)) + ) + N(4 + R(l1 (-2800 -2800) (800 4100)) + ) + X(1 AA Y(-1100 5900) + P(0 3) + P(1 2) + P(2 1) + ) + X(2 A O(90) Y(-100 -700) + P(0 4) + P(1 1) + P(2 2) + ) +) From a0d7ce0cd8b17bedd99904c2e90768de64eec900 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Jun 2024 22:42:06 +0200 Subject: [PATCH 11/12] Maybe fixing two basic issues in debugger: - desktop deadlock if using the help window's search function: That is because we stole some events in the debugger. - disabling paint events during debugging The patch is simply to take out the code responsible for this. This *may* create issues as debugging of event handlers may create recursive debugger calls. Needs to be investigated. But the overall effect so far is positive. --- src/lay/lay/layMacroEditorDialog.cc | 58 +++++++++++++++++------------ 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/src/lay/lay/layMacroEditorDialog.cc b/src/lay/lay/layMacroEditorDialog.cc index ed07e8e17d..25a90944ad 100644 --- a/src/lay/lay/layMacroEditorDialog.cc +++ b/src/lay/lay/layMacroEditorDialog.cc @@ -1507,29 +1507,39 @@ MacroEditorDialog::eventFilter (QObject *obj, QEvent *event) return false; } - if (lay::BusySection::is_busy () && (m_in_breakpoint || m_in_exec) && (dynamic_cast (event) != 0 || dynamic_cast (event) != 0)) { - - // In breakpoint or execution mode and while processing the events from the debugger, - // ignore all input or paint events targeted to widgets which are not children of this or the assistant dialog. - // Ignoring the paint event is required because otherwise a repaint action would be triggered on a layout which - // is potentially unstable or inconsistent. - // We nevertheless allow events send to a HelpDialog or ProgressWidget since those are vital for the application's - // functionality and are known not to cause any interference. - QObject *rec = obj; - while (rec && (rec != this && !dynamic_cast (rec) && !dynamic_cast (rec))) { - rec = rec->parent (); - } - if (! rec) { - // TODO: reschedule the paint events (?) - return true; - } + if (m_in_exec) { - } else if (! lay::BusySection::is_busy () && m_in_exec) { + if (lay::BusySection::is_busy ()) { - // While no explicit event processing is in progress and we are executing, this is an indication that - // "real" events are processed. In that case, we can postpone excplit processing. This avoids interference - // with GUI code run in the debugger. - m_last_process_events = tl::Clock::current (); +#if 0 + if (dynamic_cast (event) != 0 || dynamic_cast (event) != 0) { + + // In breakpoint or execution mode and while processing the events inside the debugger, + // ignore all input or paint events targeted to widgets which are not children of this or the assistant dialog. + // Ignoring the paint event is required because otherwise a repaint action would be triggered on a layout which + // is potentially unstable or inconsistent. + // We nevertheless allow events send to a HelpDialog or ProgressWidget since those are vital for the application's + // functionality and are known not to cause any interference. + QObject *rec = obj; + while (rec && (rec != this && !dynamic_cast (rec) && !dynamic_cast (rec))) { + rec = rec->parent (); + } + if (! rec) { + // TODO: reschedule the paint events (?) + return true; + } + + } +#endif + + } else { + + // While no explicit event processing is in progress and we are executing, this is an indication that + // "real" events are processed. In that case, we can postpone excplit processing. This avoids interference + // with GUI code run in the debugger. + m_last_process_events = tl::Clock::current (); + + } } @@ -3054,6 +3064,8 @@ MacroEditorDialog::start_exec (gsi::Interpreter *ec) return; } else if (m_ignore_exec_events) { return; + } else if (lay::BusySection::is_busy () || m_in_breakpoint) { + return; } // prevents recursion @@ -3215,7 +3227,7 @@ MacroEditorDialog::exception_thrown (gsi::Interpreter *interpreter, size_t file_ exit_if_needed (); // avoid recursive breakpoints and exception catches from the console while in a breakpoint or exception stop - if (lay::BusySection::is_busy ()) { + if (lay::BusySection::is_busy () || m_in_breakpoint) { return; } @@ -3317,7 +3329,7 @@ MacroEditorDialog::trace (gsi::Interpreter *interpreter, size_t file_id, int lin exit_if_needed (); // avoid recursive breakpoints and exception catches from the console while in a breakpoint or exception stop - if (lay::BusySection::is_busy ()) { + if (lay::BusySection::is_busy () || m_in_breakpoint) { return; } From 0a453b069c58f8dbf382adfa14ae7f97caab0966 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 28 Jun 2024 15:45:25 +0200 Subject: [PATCH 12/12] Bugfix: when debugging a modal dialog event or callback, 'run'/'stop' and 'step' was not working --- src/lay/lay/layMacroEditorDialog.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/lay/lay/layMacroEditorDialog.cc b/src/lay/lay/layMacroEditorDialog.cc index 25a90944ad..c76a8200a6 100644 --- a/src/lay/lay/layMacroEditorDialog.cc +++ b/src/lay/lay/layMacroEditorDialog.cc @@ -3694,7 +3694,7 @@ MacroEditorDialog::stop_button_clicked () { if (QApplication::activeModalWidget () == this) { // close this window if it was shown in modal mode - accept (); + QDialog::accept (); } m_in_exec = false; @@ -3760,12 +3760,9 @@ MacroEditorDialog::run (int stop_stack_depth, lym::Macro *macro) if (QApplication::activeModalWidget () == this) { // close this window if it was shown in modal mode - accept (); + QDialog::accept (); } - // in a breakpoint - m_in_breakpoint = false; - } else { if (! macro) {