From 6601d472bff3cd67f4297846a0b5422bfb86af9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?=
Date: Sat, 30 May 2020 21:45:48 +0200
Subject: [PATCH] Implemented #570 (perimeter included in antenna check) (#572)
* First implementation of the perimeter factor for antenna check, unit tests.
* Bugfix and unit tests for GSI binding of new antenna check version.
* DRC integration of perimeter-enabled antenna check.
* Enhanced DRC doc for antenna rule
---
src/db/db/dbLayoutToNetlist.cc | 9 +-
src/db/db/dbLayoutToNetlist.h | 19 +++-
src/db/db/gsiDeclDbLayoutToNetlist.cc | 26 +++++-
src/db/unit_tests/dbLayoutToNetlistTests.cc | 65 ++++++++++++++
src/drc/drc/built-in-macros/_drc_engine.rb | 31 +++++++
src/drc/drc/built-in-macros/_drc_layer.rb | 95 ++++++++------------
src/drc/drc/built-in-macros/_drc_netter.rb | 66 +++++++++++++-
src/drc/drc/built-in-macros/_drc_tags.rb | 12 +++
src/lay/lay/doc/about/drc_ref_netter.xml | 34 +++++++
testdata/algo/antenna_au1.gds | Bin 7810 -> 11330 bytes
testdata/drc/drcSimpleTests_7.drc | 17 ++++
testdata/drc/drcSimpleTests_au7.gds | Bin 3290 -> 7706 bytes
testdata/ruby/dbLayoutToNetlist.rb | 66 +++++++++++++-
13 files changed, 372 insertions(+), 68 deletions(-)
diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc
index 0cd059357..3dbcea43e 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, const db::Region &metal, double ratio, const std::vector > &diodes)
+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)
{
// TODO: that's basically too much .. we only need the clusters
if (! m_netlist_extracted) {
@@ -1211,7 +1211,14 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Reg
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal, 0);
double agate = rgate.area () * dbu * dbu;
+ if (fabs (gate_perimeter_factor) > 1e-6) {
+ agate += rgate.perimeter () * dbu * gate_perimeter_factor;
+ }
+
double ametal = rmetal.area () * dbu * dbu;
+ if (fabs (metal_perimeter_factor) > 1e-6) {
+ ametal += rmetal.perimeter () * dbu * metal_perimeter_factor;
+ }
double r = ratio;
bool skip = false;
diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h
index a90a9b4e5..13ac9fe7f 100644
--- a/src/db/db/dbLayoutToNetlist.h
+++ b/src/db/db/dbLayoutToNetlist.h
@@ -730,6 +730,14 @@ public:
* the limit ratio all metal shapes are copied to the output region as
* error markers.
*
+ * The area computation of gate and metal happens by taking the polygon
+ * area (A) and perimeter (P) into account:
+ *
+ * A(antenna) = A + P * f
+ *
+ * where f is the perimeter factor. The unit of the area factor is
+ * micrometers.
+ *
* The limit ratio can be modified by the presence of connections to
* other layers (specifically designating diodes for charge removal).
* Each of these layers will modify the ratio by adding a value of
@@ -742,7 +750,16 @@ 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, double gate_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector > &diodes = std::vector > ());
+
+ /**
+ * @brief Variant of the antennna check not using the perimeter
+ * This version uses 0 for the perimeter factor hence not taking into account the perimeter at all.
+ */
+ 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);
+ }
/**
* @brief Saves the database to the given path
diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc
index 66818ebdb..c143ffbb4 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_check (db::LayoutToNetlist *l2n, const db::Region &poly, const db::Region &metal, double ratio, const std::vector &diodes)
+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)
{
std::vector > diode_pairs;
@@ -127,7 +127,12 @@ static db::Region antenna_check (db::LayoutToNetlist *l2n, const db::Region &pol
}
- return l2n->antenna_check (poly, metal, ratio, diode_pairs);
+ return l2n->antenna_check (poly, poly_perimeter_factor, metal, metal_perimeter_factor, ratio, diode_pairs);
+}
+
+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);
}
Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
@@ -609,6 +614,23 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"# diode_layer1 increases the ratio by 50 per sqaure micrometer area:\n"
"errors = l2n.antenna(poly, metal, 10.0 [ [ diode_layer, 50.0 ] ])\n"
"@/code\n"
+ ) +
+ gsi::method_ext ("antenna_check", &antenna_check2, gsi::arg ("gate"), gsi::arg ("gate_perimeter_factor"), gsi::arg ("metal"), 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\n"
+ "\n"
+ "This version of the \\antenna_check method allows taking the perimeter of gate or metal into account. "
+ "The effective area is computed using:\n"
+ "\n"
+ "@code\n"
+ "Aeff = A + P * f\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 "
+ "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"
),
"@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 ae2ecaf4d..144edcf1f 100644
--- a/src/db/unit_tests/dbLayoutToNetlistTests.cc
+++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc
@@ -2609,6 +2609,71 @@ TEST(10_Antenna)
a4_30.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (402, 0)));
}
+ {
+ db::LayoutToNetlist l2n (&dss);
+
+ l2n.register_layer (*rpoly, "poly");
+ l2n.register_layer (*rcont, "cont");
+ l2n.register_layer (*rmetal1, "metal1");
+ l2n.register_layer (*rvia1, "via1");
+ l2n.register_layer (*rmetal2, "metal2");
+
+ // Intra-layer
+ l2n.connect (*rpoly);
+ l2n.connect (*rcont);
+ l2n.connect (*rmetal1);
+ l2n.connect (*rvia1);
+ l2n.connect (*rmetal2);
+ // Inter-layer
+ l2n.connect (*rpoly, *rcont);
+ l2n.connect (*rcont, *rmetal1);
+ l2n.connect (*rmetal1, *rvia1);
+ l2n.connect (*rvia1, *rmetal2);
+
+ l2n.extract_netlist ();
+
+ db::Region a5_5 = l2n.antenna_check (*rpoly, 0.0, *rmetal2, 1.0, 5);
+ db::Region a5_15 = l2n.antenna_check (*rpoly, 0.0, *rmetal2, 1.0, 15);
+ db::Region a5_29 = l2n.antenna_check (*rpoly, 0.0, *rmetal2, 1.0, 29);
+
+ 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)));
+ }
+
+
+ {
+ db::LayoutToNetlist l2n (&dss);
+
+ l2n.register_layer (*rpoly, "poly");
+ l2n.register_layer (*rcont, "cont");
+ l2n.register_layer (*rmetal1, "metal1");
+ l2n.register_layer (*rvia1, "via1");
+ l2n.register_layer (*rmetal2, "metal2");
+
+ // Intra-layer
+ l2n.connect (*rpoly);
+ l2n.connect (*rcont);
+ l2n.connect (*rmetal1);
+ l2n.connect (*rvia1);
+ l2n.connect (*rmetal2);
+ // Inter-layer
+ l2n.connect (*rpoly, *rcont);
+ l2n.connect (*rcont, *rmetal1);
+ l2n.connect (*rmetal1, *rvia1);
+ l2n.connect (*rvia1, *rmetal2);
+
+ l2n.extract_netlist ();
+
+ db::Region a6_3 = l2n.antenna_check (*rpoly, 0.3, *rmetal2, 0.0, 3);
+ db::Region a6_5 = l2n.antenna_check (*rpoly, 0.3, *rmetal2, 0.0, 5);
+ db::Region a6_9 = l2n.antenna_check (*rpoly, 0.3, *rmetal2, 0.0, 9);
+
+ 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)));
+ }
+
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb
index d31cb68bc..0a185d477 100644
--- a/src/drc/drc/built-in-macros/_drc_engine.rb
+++ b/src/drc/drc/built-in-macros/_drc_engine.rb
@@ -121,6 +121,14 @@ module DRC
DRCAsDots::new(false)
end
+ def area_only(r)
+ DRCAreaAndPerimeter::new(r, 0.0)
+ end
+
+ def area_and_perimeter(r, f)
+ DRCAreaAndPerimeter::new(r, f)
+ end
+
# %DRC%
# @brief Defines SPICE output format (with options)
# @name write_spice
@@ -1767,6 +1775,29 @@ CODE
@l2ndb_index = i
end
+ def _prep_value(a)
+ if a.is_a?(RBA::DPoint)
+ RBA::Point::from_dpoint(a * (1.0 / self.dbu.to_f))
+ elsif a.is_a?(RBA::DCplxTrans)
+ RBA::ICplxTrans::from_dtrans(RBA::DCplxTrans::new(1.0 / self.dbu.to_f) * a * RBA::DCplxTrans::new(self.dbu.to_f))
+ elsif a.is_a?(RBA::DTrans)
+ RBA::ICplxTrans::from_dtrans(RBA::DCplxTrans::new(1.0 / self.dbu.to_f) * RBA::DCplxTrans::new(a) * RBA::DCplxTrans::new(self.dbu.to_f))
+ elsif a.is_a?(Float)
+ (0.5 + a / self.dbu).floor.to_i
+ else
+ a
+ end
+ end
+
+ def _prep_value_area(a)
+ dbu2 = self.dbu.to_f * self.dbu.to_f
+ if a.is_a?(Float)
+ (0.5 + a / dbu2).floor.to_i
+ else
+ a
+ end
+ end
+
private
def _make_string(v)
diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb
index 089cb7115..64033a89a 100644
--- a/src/drc/drc/built-in-macros/_drc_layer.rb
+++ b/src/drc/drc/built-in-macros/_drc_layer.rb
@@ -253,12 +253,12 @@ module DRC
if args.size == 1
a = args[0]
if a.is_a?(Range)
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, prep_value_area(a.first), prep_value_area(a.last), #{inv.inspect}))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(a.first), @engine._prep_value_area(a.last), #{inv.inspect}))
else
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, prep_value_area(a), #{inv.inspect}))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(a), #{inv.inspect}))
end
elsif args.size == 2
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, prep_value_area(args[0]), prep_value_area(args[1]), #{inv.inspect}))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value_area(args[0]), @engine._prep_value_area(args[1]), #{inv.inspect}))
else
raise("Invalid number of arguments for method '#{mn}'")
end
@@ -403,12 +403,12 @@ CODE
if args.size == 1
a = args[0]
if a.is_a?(Range)
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, prep_value(a.first), prep_value(a.last), #{inv.inspect}))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value(a.first), @engine._prep_value(a.last), #{inv.inspect}))
else
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, prep_value(a), #{inv.inspect}))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value(a), #{inv.inspect}))
end
elsif args.size == 2
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, prep_value(args[0]), prep_value(args[1]), #{inv.inspect}))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :with_#{f}, @engine._prep_value(args[0]), @engine._prep_value(args[1]), #{inv.inspect}))
else
raise("Invalid number of arguments for method '#{mn}'")
end
@@ -452,12 +452,12 @@ CODE
if args.size == 1
a = args[0]
if a.is_a?(Range)
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :with_#{f}, prep_value(a.first), prep_value(a.last), #{inv.inspect}))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(a.first), @engine._prep_value(a.last), #{inv.inspect}))
else
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :with_#{f}, prep_value(a), #{inv.inspect}))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(a), #{inv.inspect}))
end
elsif args.size == 2
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :with_#{f}, prep_value(args[0]), prep_value(args[1]), #{inv.inspect}))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :with_#{f}, @engine._prep_value(args[0]), @engine._prep_value(args[1]), #{inv.inspect}))
else
raise("Invalid number of arguments for method '#{mn}'")
end
@@ -562,7 +562,7 @@ CODE
def rounded_corners(inner, outer, n)
requires_region("rounded_corners")
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :rounded_corners, prep_value(inner), prep_value(outer), n))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :rounded_corners, @engine._prep_value(inner), @engine._prep_value(outer), n))
end
# %DRC%
@@ -579,7 +579,7 @@ CODE
def smoothed(d)
requires_region("smoothed")
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :smoothed, prep_value(d)))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :smoothed, @engine._prep_value(d)))
end
# %DRC%
@@ -1106,9 +1106,9 @@ CODE
def ongrid(*args)
requires_region("ongrid")
if args.size == 1
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::EdgePairs, :grid_check, prep_value(args[0]), prep_value(args[0])))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::EdgePairs, :grid_check, @engine._prep_value(args[0]), @engine._prep_value(args[0])))
elsif args.size == 2
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::EdgePairs, :grid_check, prep_value(args[0]), prep_value(args[1])))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::EdgePairs, :grid_check, @engine._prep_value(args[0]), @engine._prep_value(args[1])))
else
raise("Invalid number of arguments for method 'ongrid'")
end
@@ -1143,14 +1143,14 @@ CODE
requires_region("#{f}")
gx = gy = 0
if args.size == 1
- gx = gy = prep_value(args[0])
+ gx = gy = @engine._prep_value(args[0])
elsif args.size == 2
- gx = prep_value(args[0])
- gy = prep_value(args[1])
+ gx = @engine._prep_value(args[0])
+ gy = @engine._prep_value(args[1])
else
raise("Invalid number of arguments for method 'ongrid'")
end
- aa = args.collect { |a| prep_value(a) }
+ aa = args.collect { |a| @engine._prep_value(a) }
if :#{f} == :snap && @engine.is_tiled?
# in tiled mode, no modifying versions are available
@data = @engine._tcmd(@data, 0, @data.class, :snapped, gx, gy)
@@ -2039,7 +2039,7 @@ CODE
eval <<"CODE"
def #{f}(length, fraction = 0.0)
requires_edges("#{f}")
- length = prep_value(length)
+ length = @engine._prep_value(length)
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :#{f}, length, fraction))
end
CODE
@@ -2087,16 +2087,16 @@ CODE
av = [ 0, 0, 0, 0, false ]
args.each_with_index do |a,i|
if a.is_a?(Hash)
- a[:begin] && av[0] = prep_value(a[:begin])
- a[:end] && av[1] = prep_value(a[:end])
- a[:out] && av[2] = prep_value(a[:out])
- a[:in] && av[3] = prep_value(a[:in])
+ a[:begin] && av[0] = @engine._prep_value(a[:begin])
+ a[:end] && av[1] = @engine._prep_value(a[:end])
+ a[:out] && av[2] = @engine._prep_value(a[:out])
+ a[:in] && av[3] = @engine._prep_value(a[:in])
a[:joined] && av[4] = true
elsif i < 4
if !a.is_a?(1.class) && !a.is_a?(Float)
raise("Invalid type for argument " + (i+1).to_s + " (method '#{f}')")
end
- av[i] = prep_value(a)
+ av[i] = @engine._prep_value(a)
elsif i == 4
if a.is_a?(DRCJoinFlag)
av[i] = a.value
@@ -2142,7 +2142,7 @@ CODE
eval <<"CODE"
def #{f}(dist)
requires_edges("#{f}")
- DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :#{f}, prep_value(dist)))
+ DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Region, :#{f}, @engine._prep_value(dist)))
end
CODE
end
@@ -2635,11 +2635,11 @@ CODE
elsif a.is_a?(DRCLayer)
other = a
elsif a.is_a?(DRCProjectionLimits)
- minp = prep_value(a.min)
- maxp = prep_value(a.max)
+ minp = @engine._prep_value(a.min)
+ maxp = @engine._prep_value(a.max)
elsif a.is_a?(Float) || a.is_a?(1.class)
value && raise("Value already specified")
- value = prep_value(a)
+ value = @engine._prep_value(a)
else
raise("#{f}: Parameter #" + n.to_s + " does not have an expected type")
end
@@ -2694,11 +2694,11 @@ CODE
elsif a.is_a?(DRCLayer)
other = a
elsif a.is_a?(DRCProjectionLimits)
- minp = prep_value(a.min)
- maxp = prep_value(a.max)
+ minp = @engine._prep_value(a.min)
+ maxp = @engine._prep_value(a.max)
elsif a.is_a?(Float) || a.is_a?(1.class)
value && raise("Value already specified")
- value = prep_value(a)
+ value = @engine._prep_value(a)
else
raise("#{f}: Parameter #" + n.to_s + " does not have an expected type")
end
@@ -2868,7 +2868,7 @@ CODE
values = []
args.each do |a|
if a.is_a?(1.class) || a.is_a?(Float)
- v = prep_value(a)
+ v = @engine._prep_value(a)
v.abs > dist && dist = v.abs
values.push(v)
elsif a.is_a?(DRCSizingMode)
@@ -2918,7 +2918,7 @@ CODE
def polygons(*args)
requires_edge_pairs("polygons")
args.size <= 1 || raise("polygons: Method requires 0 or 1 arguments")
- aa = args.collect { |a| prep_value(a) }
+ aa = args.collect { |a| @engine._prep_value(a) }
DRCLayer::new(@engine, @engine._cmd(@data, :polygons, *aa))
end
@@ -3007,7 +3007,7 @@ CODE
%w(extents moved transformed).each do |f|
eval <<"CODE"
def #{f}(*args)
- aa = args.collect { |a| prep_value(a) }
+ aa = args.collect { |a| @engine._prep_value(a) }
DRCLayer::new(@engine, @engine._cmd(@data, :#{f}, *aa))
end
CODE
@@ -3016,7 +3016,7 @@ CODE
%w(move transform).each do |f|
eval <<"CODE"
def #{f}(*args)
- aa = args.collect { |a| prep_value(a) }
+ aa = args.collect { |a| @engine._prep_value(a) }
@engine._cmd(@data, :#{f}, *aa)
self
end
@@ -3062,13 +3062,13 @@ CODE
def merged(*args)
requires_edges_or_region("merged")
- aa = args.collect { |a| prep_value(a) }
+ aa = args.collect { |a| @engine._prep_value(a) }
DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :merged, *aa))
end
def merge(*args)
requires_edges_or_region("merge")
- aa = args.collect { |a| prep_value(a) }
+ aa = args.collect { |a| @engine._prep_value(a) }
if @engine.is_tiled?
# in tiled mode, no modifying versions are available
@data = @engine._tcmd(@data, 0, @data.class, :merged, *aa)
@@ -3172,29 +3172,6 @@ CODE
end
end
- def prep_value(a)
- if a.is_a?(RBA::DPoint)
- RBA::Point::from_dpoint(a * (1.0 / @engine.dbu.to_f))
- elsif a.is_a?(RBA::DCplxTrans)
- RBA::ICplxTrans::from_dtrans(RBA::DCplxTrans::new(1.0 / @engine.dbu.to_f) * a * RBA::DCplxTrans::new(@engine.dbu.to_f))
- elsif a.is_a?(RBA::DTrans)
- RBA::ICplxTrans::from_dtrans(RBA::DCplxTrans::new(1.0 / @engine.dbu.to_f) * RBA::DCplxTrans::new(a) * RBA::DCplxTrans::new(@engine.dbu.to_f))
- elsif a.is_a?(Float)
- (0.5 + a / @engine.dbu).floor.to_i
- else
- a
- end
- end
-
- def prep_value_area(a)
- dbu2 = @engine.dbu.to_f * @engine.dbu.to_f
- if a.is_a?(Float)
- (0.5 + a / dbu2).floor.to_i
- else
- a
- end
- end
-
end
end
diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb
index bfc95d8cc..4006c2054 100644
--- a/src/drc/drc/built-in-macros/_drc_netter.rb
+++ b/src/drc/drc/built-in-macros/_drc_netter.rb
@@ -336,16 +336,74 @@ module DRC
# Multiple diode specifications are allowed. Just add them
# to the antenna_check call.
#
+ # You can include the perimeter into the area computation for
+ # the gate or metal layer or both. The physical picture
+ # is this: the side walls of the material contribute to the
+ # surface too. As the side wall area can be estimated by taking
+ # the perimeter times some material thickness, the effective
+ # area is:
+ #
+ # @code
+ # A(eff) = A + P * t
+ # @/code
+ #
+ # Here A is the area of the polygons and P is their perimeter.
+ # t is the "thickness" in micrometer units. To specify such
+ # a condition, use the following notation:
+ #
+ # @code
+ # errors = antenna_check(area_and_perimeter(gate, 0.5), ...)
+ # @/code
+ #
+ # "area_and_perimeter" takes the polygon layer and the
+ # thickness (0.5 micrometers in this case).
+ # This notation can be applied to both gate and
+ # metal layers. A detailed notation for the usual,
+ # area-only case is available as well for completeness:
+ #
+ # @code
+ # errors = antenna_check(area_only(gate), ...)
+ #
+ # # this is equivalent to a zero thickness:
+ # errors = antenna_check(area_and_perimeter(gate, 0.0), ...)
+ # # or the standard case:
+ # errors = antenna_check(gate, ...)
+ # @/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.
- def antenna_check(gate, metal, ratio, *diodes)
+ def antenna_check(agate, ametal, ratio, *diodes)
+
+ gate_perimeter_factor = 0.0
+ if agate.is_a?(DRC::DRCLayer)
+ gate = agate
+ elsif agate.is_a?(DRC::DRCAreaAndPerimeter)
+ gate = agate.region
+ gate_perimeter_factor = agate.perimeter_factor
+ if ! gate.is_a?(DRC::DRCLayer)
+ raise("gate with area or area_and_perimeter: input argument must be a layer")
+ end
+ else
+ raise("gate argument of Netter#antenna_check must be a layer ")
+ end
- gate.is_a?(DRC::DRCLayer) || raise("gate argument of Netter#antenna_check must be a layer")
gate.requires_region("Netter#antenna_check (gate argument)")
- metal.is_a?(DRC::DRCLayer) || raise("metal argument of Netter#antenna_check must be a layer")
+ metal_perimeter_factor = 0.0
+ if ametal.is_a?(DRC::DRCLayer)
+ metal = ametal
+ elsif ametal.is_a?(DRC::DRCAreaAndPerimeter)
+ metal = ametal.region
+ metal_perimeter_factor = ametal.perimeter_factor
+ if ! metal.is_a?(DRC::DRCLayer)
+ raise("metal with area or area_and_perimeter: input argument must be a layer")
+ end
+ else
+ raise("metal argument of Netter#antenna_check must be a layer")
+ end
+
metal.requires_region("Netter#antenna_check (metal argument)")
if !ratio.is_a?(1.class) && !ratio.is_a?(Float)
@@ -363,7 +421,7 @@ module DRC
end
end
- DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, metal.data, ratio, dl))
+ DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, gate_perimeter_factor, metal.data, 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 d8f19dfae..9584adcf4 100644
--- a/src/drc/drc/built-in-macros/_drc_tags.rb
+++ b/src/drc/drc/built-in-macros/_drc_tags.rb
@@ -112,5 +112,17 @@ module DRC
end
end
+ # A wrapper for an input for the antenna check
+ # This class is used to identify a region plus an
+ # optional perimeter factor
+ class DRCAreaAndPerimeter
+ attr_accessor :region
+ attr_accessor :perimeter_factor
+ def initialize(r, f)
+ self.region = r
+ self.perimeter_factor = f
+ end
+ 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 a08620943..39eb2a6a0 100644
--- a/src/lay/lay/doc/about/drc_ref_netter.xml
+++ b/src/lay/lay/doc/about/drc_ref_netter.xml
@@ -133,6 +133,40 @@ errors = antenna_check(gate, metal1, 50.0, [ diode, 10.0 ])
Multiple diode specifications are allowed. Just add them
to the antenna_check call.
+You can include the perimeter into the area computation for
+the gate or metal layer or both. The physical picture
+is this: the side walls of the material contribute to the
+surface too. As the side wall area can be estimated by taking
+the perimeter times some material thickness, the effective
+area is:
+
+
+A(eff) = A + P * t
+
+
+Here A is the area of the polygons and P is their perimeter.
+t is the "thickness" in micrometer units. To specify such
+a condition, use the following notation:
+
+
+errors = antenna_check(area_and_perimeter(gate, 0.5), ...)
+
+
+"area_and_perimeter" takes the polygon layer and the
+thickness (0.5 micrometers in this case).
+This notation can be applied to both gate and
+metal layers. A detailed notation for the usual,
+area-only case is available as well for completeness:
+
+
+errors = antenna_check(area_only(gate), ...)
+
+# this is equivalent to a zero thickness:
+errors = antenna_check(area_and_perimeter(gate, 0.0), ...)
+# or the standard case:
+errors = antenna_check(gate, ...)
+
+
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 0367b454c514a474fc21eaa3dc44abbf76a99233..9c56d0a66d5240862eb0eb9382ecc46b4daba1b6 100644
GIT binary patch
delta 554
zcmaKou}%U(6h+^RyTh8SgcwDlgzyKJU?GIfHbxUID0a5ce<3uMW=b2a_y8JfLi`dx
zz{0Vh^1XLSw884k%)MtaZ)TUPswPL)R@61N@j)q_QJV&o|6NmN-OW`&bhMX_CXa*b
z_r>Q$`F8&DTu-T+?)0AO(A#;XbJN?q@M?~ieY0t08Ie1%<|`EYBf%_FSfJRV`CuX%
z5setM!G|1uhgE*hvuJ}McQB+Ohk#Z*DjpS&iich^s$PTsn`}8eEc4$t&)fRgHISU>
zt_0@58b~_GPRBfmYB8u5gK9CT#+mdv161RTZJtCthIkC|7~)9=h$k5Y^*lTe&%^WZ
SI1_<0Kt0YnL&qPHB=iFw5rW(R
delta 181
zcmX>U(PSINz{bGD6u}_F$i)7bfti7kL7ah=K>?Y~z`?}kJodKUon}3MA@UQ{Zu&^)y
E0ISvC#R0Hks&EGS&*nfi@oN6;J_rTy(LDXA
zmz{}L9ozwJh8~~-a-dC1y>)xhiTd;aymEn?#LmWZV{hEVc5gg4_QnmS-dh5=d#Z!(
zSbqV_fX%TEdQ&~w#A|SFH}C?W0&<}3!T0PHI_P5#JMzJu=g_rFxY=>bZx)EJXSO0TO$b_TVBDB!iBGkf$nu}1QvqfhNJ7bvADcuF#
Q1>J&f0rw=sf1D)p7qO$qpa1{>
delta 354
zcmZ{fJxjw-6oyZ3?nkvv0-{K15nS{RLZMKLBK0=7lt>X35#38hp}WqG;cU&~q}{vt
z3mlxC>?o8B{Sm%)a>?*;&hWhFV{-CcelUtjizS47fKi5aZcQAO*)@TQ$Dce2Gn_8DY(i@(Gc
z?|bd-lCx$H-l4|};doDVcrFuul`*H%u?229AaUQeZ)$
x&{xNNUv=E5Ii92DwPZCv4-+1R$c69%