From 261fb027fd6dde27d6cced4ea7e42ebae5d2b721 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 2 Mar 2019 00:38:51 +0100 Subject: [PATCH] GSI binding of antenna check function + tests. --- src/db/db/dbLayoutToNetlist.cc | 4 +- src/db/db/dbLayoutToNetlist.h | 2 +- src/db/db/dbRegion.h | 3 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 74 ++++++++ src/db/db/gsiDeclDbRegion.cc | 2 + src/db/unit_tests/dbLayoutToNetlistTests.cc | 4 +- testdata/ruby/dbLayoutToNetlist.rb | 195 ++++++++++++++++++++ 7 files changed, 278 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 125e96e77..07600bf5a 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -862,7 +862,7 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin } } -db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector > &diodes) +db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector > &diodes) { // TODO: that's basically too much .. we only need the clusters if (! m_netlist_extracted) { @@ -898,7 +898,7 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Reg double r = ratio; bool skip = false; - for (std::vector >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) { + for (std::vector >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) { db::Region rdiode; deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode); diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 497cddef8..e85ec4a68 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -523,7 +523,7 @@ public: * regardless of the diode's area. * In other words: any diode will make the net safe against antenna discharge. */ - db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector > &diodes = std::vector > ()); + db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector > &diodes = std::vector > ()); private: // no copying diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 5aa72d6d8..0a9f1a857 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -1426,9 +1426,10 @@ public: * * This method will turn any region into a flat shape collection. */ - void flatten () + db::Region &flatten () { flat_region (); + return *this; } /** diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 92884f92e..3968293ea 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -25,6 +25,7 @@ #include "dbLayoutToNetlistWriter.h" #include "dbLayoutToNetlistReader.h" #include "tlStream.h" +#include "tlVariant.h" namespace gsi { @@ -104,6 +105,38 @@ static std::vector l2n_layer_names (const db::LayoutToNetlist *l2n) return ln; } +static db::Region antenna_check (db::LayoutToNetlist *l2n, const db::Region &poly, const db::Region &metal, double ratio, const std::vector &diodes) +{ + std::vector > diode_pairs; + + for (std::vector::const_iterator d = diodes.begin (); d != diodes.end (); ++d) { + + if (d->is_user ()) { + + diode_pairs.push_back (std::make_pair (& d->to_user (), 0.0)); + + } else if (d->is_list ()) { + + const std::vector &list = d->get_list (); + if (list.size () != 2) { + throw tl::Exception (tl::to_string (tr ("Diode layer specifications of 'antenna' method require list of diode layer/ratio pairs (e.g. '[ [ diode_layer, 10.0 ], ... ]')"))); + } + if (! list [0].is_user ()) { + throw tl::Exception (tl::to_string (tr ("Diode layer specifications of 'antenna' method require list of diode layer/ratio pairs (e.g. '[ [ diode_layer, 10.0 ], ... ]') - first element isn't a Region object"))); + } + if (! list [1].can_convert_to_double ()) { + throw tl::Exception (tl::to_string (tr ("Diode layer specifications of 'antenna' method require list of diode layer/ratio pairs (e.g. '[ [ diode_layer, 10.0 ], ... ]') - second element isn't a number"))); + } + + diode_pairs.push_back (std::make_pair (& list [0].to_user (), list [1].to_double ())); + + } + + } + + return l2n->antenna_check (poly, metal, ratio, diode_pairs); +} + Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::constructor ("new", &make_l2n, gsi::arg ("iter"), "@brief Creates a new extractor connected to an original layout\n" @@ -386,6 +419,47 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method_ext ("read", &read_l2n, gsi::arg ("path"), "@brief Reads the extracted netlist from the file.\n" "This method employs the native format of KLayout.\n" + ) + + gsi::method_ext ("antenna_check", &antenna_check, gsi::arg ("gate"), gsi::arg ("metal"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector ()), + "@brief Runs an antenna check on the extracted clusters\n" + "\n" + "The antenna check will traverse all clusters and run an antenna check\n" + "for all root clusters. The antenna ratio is defined by the total\n" + "area of all \"metal\" shapes divided by the total area of all \"gate\" shapes\n" + "on the cluster. Of all clusters where the antenna ratio is larger than\n" + "the limit ratio all metal shapes are copied to the output region as\n" + "error markers.\n" + "\n" + "The simple call is:\n" + "\n" + "@code\n" + "l2n = ... # a LayoutToNetlist object\n" + "l2n.extract_netlist\n" + "# check for antenna ratio 10.0 of metal vs. poly:\n" + "errors = l2n.antenna(poly, metal, 10.0)\n" + "@endcode\n" + "\n" + "You can include diodes which rectify the antenna effect. " + "Provide recognition layers for theses diodes and include them " + "in the connections. Then specify the diode layers in the antenna call:\n" + "\n" + "@code\n" + "...\n" + "# include diode_layer1:\n" + "errors = l2n.antenna(poly, metal, 10.0, [ diode_layer1 ])\n" + "# include diode_layer1 and diode_layer2:" + "errors = l2n.antenna(poly, metal, 10.0, [ diode_layer1, diode_layer2 ])\n" + "@endcode\n" + "\n" + "Diodes can be configured to partially reduce the antenna effect depending " + "on their area. This will make the diode_layer1 increase the ratio by 50.0 " + "per square micrometer area of the diode:\n" + "\n" + "@code\n" + "...\n" + "# diode_layer1 increases the ratio by 50 per sqaure micrometer area:\n" + "errors = l2n.antenna(poly, metal, 10.0 [ [ diode_layer, 50.0 ] ])\n" + "@endcode\n" ), "@brief A generic framework for extracting netlists from layouts\n" "\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 78f7e41e4..32296d87f 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -2280,6 +2280,8 @@ Class decl_Region ("db", "Region", "If the region is already flat (i.e. \\has_valid_polygons? returns true), this method will " "not change it.\n" "\n" + "Returns 'self', so this method can be used in a dot concatenation.\n" + "\n" "This method has been introduced in version 0.26." ) + method ("has_valid_polygons?", &db::Region::has_valid_polygons, diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 7e04e113d..681dff981 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -2390,7 +2390,7 @@ TEST(10_Antenna) l2n.extract_netlist (); - std::vector > diodes; + std::vector > diodes; // 8.0 means: increase r by 8.0 for each um^2 of diode attached to a net diodes.push_back (std::make_pair (rdiode.get (), 8.0)); @@ -2423,7 +2423,7 @@ TEST(10_Antenna) l2n.extract_netlist (); - std::vector > diodes; + std::vector > diodes; // 0.0 means: skip all nets where there is a rdiode attached diodes.push_back (std::make_pair (rdiode.get (), 0.0)); diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index c6f5ac5df..b5e9ac6cc 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -445,6 +445,201 @@ END end + def test_20_Antenna + + # --- simple antenna check + + input = File.join($ut_testsrc, "testdata", "algo", "antenna_l1.gds") + ly = RBA::Layout::new + ly.read(input) + + au = File.join($ut_testsrc, "testdata", "algo", "antenna_au1.gds") + ly_au = RBA::Layout::new + ly_au.read(au) + + dss = RBA::DeepShapeStore::new + assert_equal(dss.is_singular?, false) + + rdiode = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(1, 0)), dss) + rpoly = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(6, 0)), dss) + rcont = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(8, 0)), dss) + rmetal1 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(9, 0)), dss) + rvia1 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(11, 0)), dss) + rmetal2 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(12, 0)), dss) + assert_equal(dss.is_singular?, true) + + l2n = RBA::LayoutToNetlist::new(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(rpoly, rcont) + l2n.connect(rcont, rmetal1) + + l2n.extract_netlist + + 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 + assert_equal((a1_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(100, 0)))).to_s, "") + assert_equal((a1_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(101, 0)))).to_s, "") + assert_equal((a1_30.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(102, 0)))).to_s, "") + + # --- same with flat + + l2n._destroy + + input = File.join($ut_testsrc, "testdata", "algo", "antenna_l1.gds") + ly = RBA::Layout::new + ly.read(input) + + au = File.join($ut_testsrc, "testdata", "algo", "antenna_au1.gds") + ly_au = RBA::Layout::new + ly_au.read(au) + + rfdiode = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(1, 0))) + rfpoly = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(6, 0))) + rfcont = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(8, 0))) + rfmetal1 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(9, 0))) + rfvia1 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(11, 0))) + rfmetal2 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(12, 0))) + assert_equal(rfdiode.is_deep?, false) + assert_equal(rfpoly.is_deep?, false) + assert_equal(rfmetal1.is_deep?, false) + assert_equal(rfvia1.is_deep?, false) + assert_equal(rfmetal2.is_deep?, false) + + l2n = RBA::LayoutToNetlist::new(ly.top_cell.name, ly.dbu) + + l2n.register(rfdiode, "diode") + l2n.register(rfpoly, "poly") + l2n.register(rfcont, "cont") + l2n.register(rfmetal1, "metal1") + l2n.register(rfvia1, "via1") + l2n.register(rfmetal2, "metal2") + + l2n.connect(rfpoly) + l2n.connect(rfcont) + l2n.connect(rfmetal1) + l2n.connect(rfpoly, rfcont) + l2n.connect(rfcont, rfmetal1) + + l2n.extract_netlist + + a1_3 = l2n.antenna_check(rfpoly, rfmetal1, 3) + a1_10 = l2n.antenna_check(rfpoly, rfmetal1, 10) + a1_30 = l2n.antenna_check(rfpoly, rfmetal1, 30) + + # Note: flatten.merged performs some normalization + assert_equal((a1_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(100, 0)))).to_s, "") + assert_equal((a1_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(101, 0)))).to_s, "") + assert_equal((a1_30.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(102, 0)))).to_s, "") + + # --- simple antenna check with metal2 + + l2n._destroy + l2n = RBA::LayoutToNetlist::new(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) + l2n.connect(rmetal1, rvia1) + l2n.connect(rvia1, rmetal2) + + l2n.extract_netlist + + 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 + assert_equal((a2_5.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(200, 0)))).to_s, "") + assert_equal((a2_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(201, 0)))).to_s, "") + assert_equal((a2_17.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(202, 0)))).to_s, "") + + # --- antenna check with diodes and antenna effect reduction + + l2n._destroy + l2n = RBA::LayoutToNetlist::new(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(rdiode) + l2n.connect(rpoly) + l2n.connect(rcont) + l2n.connect(rmetal1) + l2n.connect(rdiode, rcont) + l2n.connect(rpoly, rcont) + l2n.connect(rcont, rmetal1) + + l2n.extract_netlist + + a3_3 = l2n.antenna_check(rpoly, rmetal1, 3, [ [ rdiode, 8.0 ] ] ) + a3_10 = l2n.antenna_check(rpoly, rmetal1, 10, [ [ rdiode, 8.0 ] ]) + a3_30 = l2n.antenna_check(rpoly, rmetal1, 30, [ [ rdiode, 8.0 ] ]) + + # Note: flatten.merged performs some normalization + assert_equal((a3_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(300, 0)))).to_s, "") + assert_equal((a3_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(301, 0)))).to_s, "") + assert_equal((a3_30.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(302, 0)))).to_s, "") + + # --- antenna check with diodes + + l2n._destroy + l2n = RBA::LayoutToNetlist::new(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(rdiode) + l2n.connect(rpoly) + l2n.connect(rcont) + l2n.connect(rmetal1) + l2n.connect(rdiode, rcont) + l2n.connect(rpoly, rcont) + l2n.connect(rcont, rmetal1) + + l2n.extract_netlist + + a4_3 = l2n.antenna_check(rpoly, rmetal1, 3, [ rdiode ] ) + a4_10 = l2n.antenna_check(rpoly, rmetal1, 10, [ rdiode ]) + a4_30 = l2n.antenna_check(rpoly, rmetal1, 30, [ rdiode ]) + + # Note: flatten.merged performs some normalization + assert_equal((a4_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(400, 0)))).to_s, "") + assert_equal((a4_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(401, 0)))).to_s, "") + assert_equal((a4_30.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(402, 0)))).to_s, "") + + end + end load("test_epilogue.rb")