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