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