From 2d0a9418f9622a465bf8ebbd3a676aa43c4a4e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Fri, 5 Jun 2020 10:55:07 +0200 Subject: [PATCH] Implemented #579 (perimeter_only mode for antenna check) (#582) * WIP: added basic feature and tests. * WIP: provide tests are GSI binding of new antenna check * Fixed issue #579 (perimeter_only mode for antenna check) * Updated DRC doc for 'perimeter_only' --- src/db/db/dbLayoutToNetlist.cc | 12 +++++-- src/db/db/dbLayoutToNetlist.h | 23 ++++++++++--- src/db/db/gsiDeclDbLayoutToNetlist.cc | 34 ++++++++++++++++---- src/db/unit_tests/dbLayoutToNetlistTests.cc | 19 ++++++++++- src/drc/drc/built-in-macros/_drc_engine.rb | 8 +++-- src/drc/drc/built-in-macros/_drc_netter.rb | 18 ++++++++++- src/drc/drc/built-in-macros/_drc_tags.rb | 6 ++-- src/lay/lay/doc/about/drc_ref_netter.xml | 12 +++++++ testdata/algo/antenna_au1.gds | Bin 11330 -> 14850 bytes testdata/drc/drcSimpleTests_7.drc | 7 ++++ testdata/drc/drcSimpleTests_au7.gds | Bin 7706 -> 10074 bytes testdata/ruby/dbLayoutToNetlist.rb | 18 +++++++++++ 12 files changed, 138 insertions(+), 19 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 3dbcea43e..9e2d164d5 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -1180,7 +1180,7 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin } } -db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector > &diodes) +db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector > &diodes) { // TODO: that's basically too much .. we only need the clusters if (! m_netlist_extracted) { @@ -1210,12 +1210,18 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_p deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate, 0); deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal, 0); - double agate = rgate.area () * dbu * dbu; + double agate = 0.0; + if (fabs (gate_area_factor) > 1e-6) { + agate += rgate.area () * dbu * dbu * gate_area_factor; + } if (fabs (gate_perimeter_factor) > 1e-6) { agate += rgate.perimeter () * dbu * gate_perimeter_factor; } - double ametal = rmetal.area () * dbu * dbu; + double ametal = 0.0; + if (fabs (metal_area_factor) > 1e-6) { + ametal += rmetal.area () * dbu * dbu * metal_area_factor; + } if (fabs (metal_perimeter_factor) > 1e-6) { ametal += rmetal.perimeter () * dbu * metal_perimeter_factor; } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 13ac9fe7f..63bf835aa 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -733,9 +733,9 @@ public: * The area computation of gate and metal happens by taking the polygon * area (A) and perimeter (P) into account: * - * A(antenna) = A + P * f + * A(antenna) = A + P * t * - * where f is the perimeter factor. The unit of the area factor is + * where t is the perimeter factor. The unit of this area factor is * micrometers. * * The limit ratio can be modified by the presence of connections to @@ -750,7 +750,10 @@ 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, double gate_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector > &diodes = std::vector > ()); + db::Region antenna_check (const db::Region &gate, double gate_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector > &diodes = std::vector > ()) + { + return antenna_check (gate, 1.0, gate_perimeter_factor, metal, 1.0, metal_perimeter_factor, ratio, diodes); + } /** * @brief Variant of the antennna check not using the perimeter @@ -758,9 +761,21 @@ public: */ db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector > &diodes = std::vector > ()) { - return antenna_check (gate, 0.0, metal, 0.0, ratio, diodes); + return antenna_check (gate, 1.0, 0.0, metal, 1.0, 0.0, ratio, diodes); } + /** + * @brief Variant of the antenna check providing an area scale factor + * + * This version provides an additional area scale factor f, so the effective area becomes + * + * A(antenna) = A * f + P * t + * + * where f is the area scale factor and t the perimeter scale factor. This version allows to ignore the + * area contribution entirely and switch to a perimeter-based antenna check by setting f to zero. + */ + db::Region antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector > &diodes = std::vector > ()); + /** * @brief Saves the database to the given path * diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 3c0b85fd0..1e5cce32d 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -98,7 +98,7 @@ static std::vector l2n_layer_names (const db::LayoutToNetlist *l2n) return ln; } -static db::Region antenna_check2 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector &diodes) +static db::Region antenna_check3 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_area_factor, double poly_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector &diodes) { std::vector > diode_pairs; @@ -127,12 +127,17 @@ static db::Region antenna_check2 (db::LayoutToNetlist *l2n, const db::Region &po } - return l2n->antenna_check (poly, poly_perimeter_factor, metal, metal_perimeter_factor, ratio, diode_pairs); + return l2n->antenna_check (poly, poly_area_factor, poly_perimeter_factor, metal, metal_area_factor, metal_perimeter_factor, ratio, diode_pairs); +} + +static db::Region antenna_check2 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector &diodes) +{ + return antenna_check3 (l2n, poly, 1, poly_perimeter_factor, metal, 1, metal_perimeter_factor, ratio, diodes); } static db::Region antenna_check (db::LayoutToNetlist *l2n, const db::Region &poly, const db::Region &metal, double ratio, const std::vector &diodes) { - return antenna_check2 (l2n, poly, 0, metal, 0, ratio, diodes); + return antenna_check3 (l2n, poly, 1, 0, metal, 1, 0, ratio, diodes); } Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", @@ -617,15 +622,32 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "The effective area is computed using:\n" "\n" "@code\n" - "Aeff = A + P * f\n" + "Aeff = A + P * t\n" "@/code\n" "\n" - "Here Aeff is the area used in the check, A is the polygon area, P the perimeter and f the perimeter factor. " - "This formula applies to gate polygon area/perimeter with 'gate_perimeter_factor' for f and metal polygon area/perimeter " + "Here Aeff is the area used in the check, A is the polygon area, P the perimeter and t the perimeter factor. " + "This formula applies to gate polygon area/perimeter with 'gate_perimeter_factor' for t and metal polygon area/perimeter " "with 'metal_perimeter_factor'. The perimeter_factor has the dimension of micrometers and can be thought of as the width " "of the material. Essentially the side walls of the material are taking into account for the surface area as well.\n" "\n" "This variant has been introduced in version 0.26.6.\n" + ) + + gsi::method_ext ("antenna_check", &antenna_check3, gsi::arg ("gate"), gsi::arg ("gate_area_factor"), gsi::arg ("gate_perimeter_factor"), gsi::arg ("metal"), gsi::arg ("metal_area_factor"), gsi::arg ("metal_perimeter_factor"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector (), "[]"), + "@brief Runs an antenna check on the extracted clusters taking the perimeter into account and providing an area factor\n" + "\n" + "This (most generic) version of the \\antenna_check method allows taking the perimeter of gate or metal into account and also " + "provides a scaling factor for the area part.\n" + "The effective area is computed using:\n" + "\n" + "@code\n" + "Aeff = A * f + P * t\n" + "@/code\n" + "\n" + "Here f is the area factor and t the perimeter factor. A is the polygon area and P the polygon perimeter. " + "A use case for this variant is to set the area factor to zero. This way, only perimeter contributions are " + "considered.\n" + "\n" + "This variant has been introduced in version 0.26.6.\n" ), "@brief A generic framework for extracting netlists from layouts\n" "\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 144edcf1f..3e886b16a 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -2639,8 +2639,16 @@ TEST(10_Antenna) a5_5.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (500, 0))); a5_15.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (501, 0))); a5_29.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (502, 0))); - } + // with area factor + db::Region b5_5 = l2n.antenna_check (*rpoly, 2.0, 0.0, *rmetal2, 1.0, 1.0, 2.5); + db::Region b5_15 = l2n.antenna_check (*rpoly, 2.0, 0.0, *rmetal2, 1.0, 1.0, 7.5); + db::Region b5_29 = l2n.antenna_check (*rpoly, 2.0, 0.0, *rmetal2, 1.0, 1.0, 14.5); + + b5_5.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (550, 0))); + b5_15.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (551, 0))); + b5_29.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (552, 0))); + } { db::LayoutToNetlist l2n (&dss); @@ -2672,6 +2680,15 @@ TEST(10_Antenna) a6_3.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (600, 0))); a6_5.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (601, 0))); a6_9.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (602, 0))); + + // with area factor + db::Region b6_3 = l2n.antenna_check (*rpoly, 1.0, 0.3, *rmetal2, 2.0, 0.0, 6); + db::Region b6_5 = l2n.antenna_check (*rpoly, 1.0, 0.3, *rmetal2, 2.0, 0.0, 10); + db::Region b6_9 = l2n.antenna_check (*rpoly, 1.0, 0.3, *rmetal2, 2.0, 0.0, 18); + + b6_3.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (650, 0))); + b6_5.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (651, 0))); + b6_9.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (652, 0))); } std::string au = tl::testsrc (); diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 0a185d477..6c6b6186a 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -122,11 +122,15 @@ module DRC end def area_only(r) - DRCAreaAndPerimeter::new(r, 0.0) + DRCAreaAndPerimeter::new(r, 1.0, 0.0) + end + + def perimeter_only(r, f) + DRCAreaAndPerimeter::new(r, 0.0, f) end def area_and_perimeter(r, f) - DRCAreaAndPerimeter::new(r, f) + DRCAreaAndPerimeter::new(r, 1.0, f) end # %DRC% diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 4006c2054..2c32640ad 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -370,6 +370,18 @@ module DRC # errors = antenna_check(gate, ...) # @/code # + # Finally there is also "perimeter_only". When using this + # specification with a thickness value, the area is computed + # from the perimeter alone: + # + # @code + # A(eff) = P * t + # @/code + # + # @code + # errors = antenna_check(perimeter_only(gate, 0.5), ...) + # @/code + # # The error shapes produced by the antenna check are copies # of the metal shapes on the metal layers of each network # violating the antenna rule. @@ -377,11 +389,13 @@ module DRC def antenna_check(agate, ametal, ratio, *diodes) gate_perimeter_factor = 0.0 + gate_area_factor = 1.0 if agate.is_a?(DRC::DRCLayer) gate = agate elsif agate.is_a?(DRC::DRCAreaAndPerimeter) gate = agate.region gate_perimeter_factor = agate.perimeter_factor + gate_area_factor = agate.area_factor if ! gate.is_a?(DRC::DRCLayer) raise("gate with area or area_and_perimeter: input argument must be a layer") end @@ -392,11 +406,13 @@ module DRC gate.requires_region("Netter#antenna_check (gate argument)") metal_perimeter_factor = 0.0 + metal_area_factor = 1.0 if ametal.is_a?(DRC::DRCLayer) metal = ametal elsif ametal.is_a?(DRC::DRCAreaAndPerimeter) metal = ametal.region metal_perimeter_factor = ametal.perimeter_factor + metal_area_factor = ametal.area_factor if ! metal.is_a?(DRC::DRCLayer) raise("metal with area or area_and_perimeter: input argument must be a layer") end @@ -421,7 +437,7 @@ module DRC end end - DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, gate_perimeter_factor, metal.data, metal_perimeter_factor, ratio, dl)) + DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, gate_area_factor, gate_perimeter_factor, metal.data, metal_area_factor, metal_perimeter_factor, ratio, dl)) end diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 9584adcf4..4cbe21533 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -117,10 +117,12 @@ module DRC # optional perimeter factor class DRCAreaAndPerimeter attr_accessor :region + attr_accessor :area_factor attr_accessor :perimeter_factor - def initialize(r, f) + def initialize(r, f, t) self.region = r - self.perimeter_factor = f + self.area_factor = f + self.perimeter_factor = t end end diff --git a/src/lay/lay/doc/about/drc_ref_netter.xml b/src/lay/lay/doc/about/drc_ref_netter.xml index 39eb2a6a0..6d4a91380 100644 --- a/src/lay/lay/doc/about/drc_ref_netter.xml +++ b/src/lay/lay/doc/about/drc_ref_netter.xml @@ -167,6 +167,18 @@ errors = antenna_check(area_and_perimeter(gate, 0.0), ...) errors = antenna_check(gate, ...)

+Finally there is also "perimeter_only". When using this +specification with a thickness value, the area is computed +from the perimeter alone: +

+

+A(eff) = P * t
+
+

+

+errors = antenna_check(perimeter_only(gate, 0.5), ...)
+
+

The error shapes produced by the antenna check are copies of the metal shapes on the metal layers of each network violating the antenna rule. diff --git a/testdata/algo/antenna_au1.gds b/testdata/algo/antenna_au1.gds index 9c56d0a66d5240862eb0eb9382ecc46b4daba1b6..7527f097c95b37f34b811cc1a2c5cfed85d995c7 100644 GIT binary patch delta 460 zcmZvWF>1p=5JhJxMKu!R~Cf&*C@jB8af7Vw-;rpWj{c%~+1AOFeB5_Mn?n*UE+2|)T8&Dw&3j+Xtj2TD( diff --git a/testdata/drc/drcSimpleTests_7.drc b/testdata/drc/drcSimpleTests_7.drc index 8ed2a0e36..28797e54b 100644 --- a/testdata/drc/drcSimpleTests_7.drc +++ b/testdata/drc/drcSimpleTests_7.drc @@ -52,3 +52,10 @@ antenna_check(area_and_perimeter(gate, 70.nm), area_only(metal2), 5.0, diode).ou antenna_check(area_and_perimeter(gate, 0.07), area_only(metal2), 10.0, diode).output(410) antenna_check(area_and_perimeter(gate, 0.07), area_only(metal2), 50.0, diode).output(450) +antenna_check(perimeter_only(gate, 0.07.um), area_only(metal2), 1.0, diode).output(501) +antenna_check(perimeter_only(gate, 0.07.um), area_only(metal2), 1.5, diode).output(502) +antenna_check(perimeter_only(gate, 0.07.um), area_only(metal2), 2.0, diode).output(503) +antenna_check(perimeter_only(gate, 70.nm), area_only(metal2), 5.0, diode).output(505) +antenna_check(perimeter_only(gate, 0.07), area_only(metal2), 10.0, diode).output(510) +antenna_check(perimeter_only(gate, 0.07), area_only(metal2), 50.0, diode).output(550) + diff --git a/testdata/drc/drcSimpleTests_au7.gds b/testdata/drc/drcSimpleTests_au7.gds index 2a6e918c27cf807704c3c992eb646f4c90b59d0b..cb53332ae815512be018c2b37a2e981ae13eb72d 100644 GIT binary patch delta 365 zcmZvUF$%&!6a{~lxCw$71-rG0$50GjAcb03}f~52c?vf_>_SqOn zGdyPZ&u#b0Q_-T-MNFWgH+CqAY-O;kbd!xf$W-g|@sOWH!brqS250}ds(h|S6OU_f zH*qYYXDayACdt5d?%Dh{X%^lTWJ9*3S<;gYXj!l&jWY(UK{EDQir$P#}5 diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index a6e3b6922..094eef598 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -798,6 +798,15 @@ END assert_equal((a5_15.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(501, 0)))).to_s, "") assert_equal((a5_29.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(502, 0)))).to_s, "") + b5_5 = l2n.antenna_check(rpoly, 2.0, 0.0, rmetal2, 1.0, 1.0, 2.5) + b5_15 = l2n.antenna_check(rpoly, 2.0, 0.0, rmetal2, 1.0, 1.0, 7.5) + b5_29 = l2n.antenna_check(rpoly, 2.0, 0.0, rmetal2, 1.0, 1.0, 14.5) + + # Note: flatten.merged performs some normalization + assert_equal((b5_5.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(500, 0)))).to_s, "") + assert_equal((b5_15.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(501, 0)))).to_s, "") + assert_equal((b5_29.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(502, 0)))).to_s, "") + # --- antenna check gate perimeter included l2n._destroy @@ -830,6 +839,15 @@ END assert_equal((a6_5.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(601, 0)))).to_s, "") assert_equal((a6_9.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(602, 0)))).to_s, "") + b6_3 = l2n.antenna_check(rpoly, 1.0, 0.3, rmetal2, 2.0, 0.0, 6) + b6_5 = l2n.antenna_check(rpoly, 1.0, 0.3, rmetal2, 2.0, 0.0, 10) + b6_9 = l2n.antenna_check(rpoly, 1.0, 0.3, rmetal2, 2.0, 0.0, 18) + + # Note: flatten.merged performs some normalization + assert_equal((b6_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(600, 0)))).to_s, "") + assert_equal((b6_5.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(601, 0)))).to_s, "") + assert_equal((b6_9.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(602, 0)))).to_s, "") + end end