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 0367b454c..9c56d0a66 100644
Binary files a/testdata/algo/antenna_au1.gds and b/testdata/algo/antenna_au1.gds differ
diff --git a/testdata/drc/drcSimpleTests_7.drc b/testdata/drc/drcSimpleTests_7.drc
index 417c0ed44..8ed2a0e36 100644
--- a/testdata/drc/drcSimpleTests_7.drc
+++ b/testdata/drc/drcSimpleTests_7.drc
@@ -26,6 +26,8 @@ connect(metal1, via1)
connect(via1, metal2)
antenna_check(gate, metal2, 1.0, diode).output(101)
+antenna_check(gate, metal2, 1.5, diode).output(102)
+antenna_check(gate, metal2, 2.0, diode).output(103)
antenna_check(gate, metal2, 5.0, diode).output(105)
antenna_check(gate, metal2, 10.0, diode).output(110)
antenna_check(gate, metal2, 50.0, diode).output(150)
@@ -35,3 +37,18 @@ antenna_check(gate, metal2, 1.0, [ diode, 5.0 ]).output(202)
antenna_check(gate, metal2, 5.0, [ diode, 5.0 ]).output(205)
antenna_check(gate, metal2, 10.0, [ diode, 5.0 ]).output(210)
antenna_check(gate, metal2, 50.0, [ diode, 5.0 ]).output(250)
+
+antenna_check(area_only(gate), area_and_perimeter(metal2, 0.2.um), 1.0, diode).output(301)
+antenna_check(area_only(gate), area_and_perimeter(metal2, 0.2.um), 1.5, diode).output(302)
+antenna_check(area_only(gate), area_and_perimeter(metal2, 0.2.um), 2.0, diode).output(303)
+antenna_check(area_only(gate), area_and_perimeter(metal2, 0.2), 5.0, diode).output(305)
+antenna_check(area_only(gate), area_and_perimeter(metal2, 0.0002.mm), 10.0, diode).output(310)
+antenna_check(area_only(gate), area_and_perimeter(metal2, 200.nm), 50.0, diode).output(350)
+
+antenna_check(area_and_perimeter(gate, 0.07.um), area_only(metal2), 1.0, diode).output(401)
+antenna_check(area_and_perimeter(gate, 0.07.um), area_only(metal2), 1.5, diode).output(402)
+antenna_check(area_and_perimeter(gate, 0.07.um), area_only(metal2), 2.0, diode).output(403)
+antenna_check(area_and_perimeter(gate, 70.nm), area_only(metal2), 5.0, diode).output(405)
+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)
+
diff --git a/testdata/drc/drcSimpleTests_au7.gds b/testdata/drc/drcSimpleTests_au7.gds
index 3fa5aff17..2a6e918c2 100644
Binary files a/testdata/drc/drcSimpleTests_au7.gds and b/testdata/drc/drcSimpleTests_au7.gds differ
diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb
index f161769c1..a6e3b6922 100644
--- a/testdata/ruby/dbLayoutToNetlist.rb
+++ b/testdata/ruby/dbLayoutToNetlist.rb
@@ -757,7 +757,7 @@ END
l2n.extract_netlist
- a4_3 = l2n.antenna_check(rpoly, rmetal1, 3, [ rdiode ] )
+ 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 ])
@@ -766,6 +766,70 @@ END
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, "")
+ # --- antenna check metal perimeter included
+
+ l2n._destroy
+ l2n = RBA::LayoutToNetlist::new(dss)
+
+ 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(rvia1)
+ l2n.connect(rmetal2)
+ l2n.connect(rpoly, rcont)
+ l2n.connect(rcont, rmetal1)
+ l2n.connect(rmetal1, rvia1)
+ l2n.connect(rvia1, rmetal2)
+
+ l2n.extract_netlist
+
+ a5_5 = l2n.antenna_check(rpoly, 0.0, rmetal2, 1.0, 5)
+ a5_15 = l2n.antenna_check(rpoly, 0.0, rmetal2, 1.0, 15)
+ a5_29 = l2n.antenna_check(rpoly, 0.0, rmetal2, 1.0, 29)
+
+ # Note: flatten.merged performs some normalization
+ assert_equal((a5_5.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(500, 0)))).to_s, "")
+ 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, "")
+
+ # --- antenna check gate perimeter included
+
+ l2n._destroy
+ l2n = RBA::LayoutToNetlist::new(dss)
+
+ 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(rvia1)
+ l2n.connect(rmetal2)
+ l2n.connect(rpoly, rcont)
+ l2n.connect(rcont, rmetal1)
+ l2n.connect(rmetal1, rvia1)
+ l2n.connect(rvia1, rmetal2)
+
+ l2n.extract_netlist
+
+ a6_3 = l2n.antenna_check(rpoly, 0.3, rmetal2, 0.0, 3)
+ a6_5 = l2n.antenna_check(rpoly, 0.3, rmetal2, 0.0, 5)
+ a6_9 = l2n.antenna_check(rpoly, 0.3, rmetal2, 0.0, 9)
+
+ # Note: flatten.merged performs some normalization
+ assert_equal((a6_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(600, 0)))).to_s, "")
+ 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, "")
+
end
end