diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index ac8d0adfe..461dfc01c 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -246,11 +246,21 @@ void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, co extractor.extract (dss (), m_layout_index, layers, *mp_netlist, m_net_clusters, m_device_scaling); } -void LayoutToNetlist::connect (const db::Region &l) +void LayoutToNetlist::reset_extracted () { if (m_netlist_extracted) { - throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); + + m_net_clusters.clear (); + mp_netlist.reset (0); + + m_netlist_extracted = false; + } +} + +void LayoutToNetlist::connect (const db::Region &l) +{ + reset_extracted (); if (! is_persisted (l)) { register_layer (l, make_new_name ()); @@ -265,9 +275,8 @@ void LayoutToNetlist::connect (const db::Region &l) void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b) { - if (m_netlist_extracted) { - throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); - } + reset_extracted (); + if (! is_persisted (a)) { register_layer (a, make_new_name ()); } @@ -286,9 +295,8 @@ void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::Shap size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const std::string &gn) { - if (m_netlist_extracted) { - throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); - } + reset_extracted (); + if (! is_persisted (l)) { register_layer (l, make_new_name ()); } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 08cb3eb46..00cacb774 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -359,6 +359,14 @@ public: */ void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers); + /** + * @brief Resets the extracted netlist + * + * This method will invalidate the netlist and extraction. It is called automatically when + * cone of the connect methods is called. + */ + void reset_extracted (); + /** * @brief Defines an intra-layer connection for the given layer. * The layer is either an original layer created with "make_layer" and it's variants or @@ -532,6 +540,14 @@ public: */ void set_netlist_extracted (); + /** + * @brief Gets a value indicating whether the netlist has been extracted + */ + bool is_netlist_extracted () const + { + return m_netlist_extracted; + } + /** * @brief Gets the internal DeepShapeStore object * diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 468be697c..03a842844 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -394,6 +394,18 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "If errors occur, the device extractor will contain theses errors.\n" ) + + gsi::method ("reset_extracted", &db::LayoutToNetlist::reset_extracted, + "@brief Resets the extracted netlist and enables re-extraction\n" + "This method is implicitly called when using \\connect or \\connect_global after a netlist has been extracted.\n" + "This enables incremental connect with re-extraction.\n" + "\n" + "This method has been introduced in version 0.27.1.\n" + ) + + gsi::method ("is_extracted?", &db::LayoutToNetlist::is_netlist_extracted, + "@brief Gets a value indicating whether the netlist has been extracted\n" + "\n" + "This method has been introduced in version 0.27.1.\n" + ) + gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("l"), "@brief Defines an intra-layer connection for the given layer.\n" "The layer is either an original layer created with \\make_incluidelayer and it's variants or\n" diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 05b7fb6c2..5664485b1 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -64,7 +64,6 @@ module DRC def initialize(engine) @engine = engine - @netlisted = false @connect_implicit = [] @connect_implicit_per_cell = {} @connect_explicit = [] @@ -240,7 +239,6 @@ module DRC # See \connect for more details. def clear_connections - @netlisted = false @connect_implicit = [] @connect_implicit_per_cell = {} @connect_explicit = [] @@ -544,7 +542,7 @@ module DRC ensure_data # run extraction in a timed environment - if ! @netlisted + if ! @l2n.is_extracted? # configure implicit net connections @l2n.clear_join_net_names @@ -569,7 +567,6 @@ module DRC end @engine._cmd(@l2n, :extract_netlist) - @netlisted = true end @@ -609,13 +606,13 @@ module DRC end def _l2n_data - @netlisted && self.l2n_data + @l2n && @l2n.is_extracted? && self.l2n_data end private def cleanup - @netlisted && clear_connections + @l2n && @l2n.is_extracted? && clear_connections end def ensure_data @@ -644,14 +641,13 @@ module DRC def register_layer(data) id = data.data_id + ensure_data if @layers && @layers[id] # already registered return end - ensure_data - @layers[id] = data @lnum += 1 diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 15b6d54d4..aef50bbde 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -225,6 +225,46 @@ TEST(5_FlatAntenna) db::compare_layouts (_this, layout, au, db::NoNormalization); } +TEST(5_FlatAntennaIncremental) +{ + std::string rs = tl::testdata (); + rs += "/drc/drcSimpleTests_5i.drc"; + + std::string input = tl::testdata (); + input += "/drc/antenna_l1.gds"; + + std::string au = tl::testdata (); + au += "/drc/drcSimpleTests_au5.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // 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); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (_this, layout, au, db::NoNormalization); +} + TEST(6_HierarchicalAntenna) { std::string rs = tl::testdata (); diff --git a/testdata/drc/drcSimpleTests_5.drc b/testdata/drc/drcSimpleTests_5.drc index 8f46068f5..28b655ea6 100644 --- a/testdata/drc/drcSimpleTests_5.drc +++ b/testdata/drc/drcSimpleTests_5.drc @@ -1,5 +1,5 @@ -# Hierarchical antenna check +# Flat antenna check source($drc_test_source, "RINGO") target($drc_test_target) @@ -14,6 +14,17 @@ metal2 = input(8, 0) gate = diff & poly +connect(gate, poly) +connect(poly, poly_cont) +connect(poly_cont, metal1) + +antenna_check(gate, metal1, 1.0).output(201) +antenna_check(gate, metal1, 2.0).output(202) +antenna_check(gate, metal1, 3.0).output(203) +antenna_check(gate, metal1, 4.0).output(204) + +clear_connections + connect(gate, poly) connect(poly, poly_cont) connect(poly_cont, metal1) @@ -24,3 +35,4 @@ antenna_check(gate, metal2, 1.0).output(101) antenna_check(gate, metal2, 5.0).output(105) antenna_check(gate, metal2, 10.0).output(110) antenna_check(gate, metal2, 50.0).output(150) + diff --git a/testdata/drc/drcSimpleTests_5i.drc b/testdata/drc/drcSimpleTests_5i.drc new file mode 100644 index 000000000..973cdfa7e --- /dev/null +++ b/testdata/drc/drcSimpleTests_5i.drc @@ -0,0 +1,33 @@ + +# Flat antenna check + +source($drc_test_source, "RINGO") +target($drc_test_target) + +diff = input(2, 0) +poly = input(3, 0) +contact = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +via1 = input(7, 0) +metal2 = input(8, 0) + +gate = diff & poly + +connect(gate, poly) +connect(poly, poly_cont) +connect(poly_cont, metal1) + +antenna_check(gate, metal1, 1.0).output(201) +antenna_check(gate, metal1, 2.0).output(202) +antenna_check(gate, metal1, 3.0).output(203) +antenna_check(gate, metal1, 4.0).output(204) + +connect(metal1, via1) +connect(via1, metal2) + +antenna_check(gate, metal2, 1.0).output(101) +antenna_check(gate, metal2, 5.0).output(105) +antenna_check(gate, metal2, 10.0).output(110) +antenna_check(gate, metal2, 50.0).output(150) + diff --git a/testdata/drc/drcSimpleTests_au5.gds b/testdata/drc/drcSimpleTests_au5.gds index 3578cc1c0..064e177b1 100644 Binary files a/testdata/drc/drcSimpleTests_au5.gds and b/testdata/drc/drcSimpleTests_au5.gds differ diff --git a/testdata/python/dbLayoutToNetlist.py b/testdata/python/dbLayoutToNetlist.py index 05f0e62d0..669398afe 100644 --- a/testdata/python/dbLayoutToNetlist.py +++ b/testdata/python/dbLayoutToNetlist.py @@ -576,6 +576,54 @@ end; self.assertEqual(str(a1_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(101, 0)))), "") self.assertEqual(str(a1_30.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(102, 0)))), "") + # --- simple incremental antenna check with metal1 + metal2 + + l2n._destroy() + l2n = pya.LayoutToNetlist(dss) + + l2n.register(rdiode, "diode") + l2n.register(rpoly, "poly") + l2n.register(rcont, "cont") + l2n.register(rmetal1, "metal1") + l2n.register(rvia1, "via1") + l2n.register(rmetal2, "metal2") + + l2n.connect(rpoly) + l2n.connect(rcont) + l2n.connect(rmetal1) + l2n.connect(rmetal2) + l2n.connect(rpoly, rcont) + l2n.connect(rcont, rmetal1) + + self.assertEqual(l2n.is_extracted(), False) + l2n.extract_netlist() + self.assertEqual(l2n.is_extracted(), True) + + a1_3 = l2n.antenna_check(rpoly, rmetal1, 3) + a1_10 = l2n.antenna_check(rpoly, rmetal1, 10) + a1_30 = l2n.antenna_check(rpoly, rmetal1, 30) + + # Note: flatten.merged performs some normalization + self.assertEqual(str(a1_3.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(100, 0)))), "") + self.assertEqual(str(a1_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(101, 0)))), "") + self.assertEqual(str(a1_30.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(102, 0)))), "") + + l2n.connect(rmetal1, rvia1) + l2n.connect(rvia1, rmetal2) + + self.assertEqual(l2n.is_extracted(), False) + l2n.extract_netlist() + self.assertEqual(l2n.is_extracted(), True) + + a2_5 = l2n.antenna_check(rpoly, rmetal2, 5) + a2_10 = l2n.antenna_check(rpoly, rmetal2, 10) + a2_17 = l2n.antenna_check(rpoly, rmetal2, 17) + + # Note: flatten.merged performs some normalization + self.assertEqual(str(a2_5.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(200, 0)))), "") + self.assertEqual(str(a2_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(201, 0)))), "") + self.assertEqual(str(a2_17.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(202, 0)))), "") + # --- simple antenna check with metal2 l2n._destroy() diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index 7b179a732..d2beb1c9e 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -170,7 +170,8 @@ class DBLayoutToNetlist_TestClass < TestBase # Perform netlist extraction l2n.extract_netlist - assert_equal(l2n.netlist.to_s, <