mirror of https://github.com/KLayout/klayout.git
* 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
This commit is contained in:
parent
bea3e29421
commit
6601d472bf
|
|
@ -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<std::pair<const db::Region *, double> > &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<std::pair<const db::Region *, double> > &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;
|
||||
|
|
|
|||
|
|
@ -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<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > ());
|
||||
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<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > ());
|
||||
|
||||
/**
|
||||
* @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<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > ())
|
||||
{
|
||||
return antenna_check (gate, 0.0, metal, 0.0, ratio, diodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Saves the database to the given path
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ static std::vector<std::string> 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<tl::Variant> &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<tl::Variant> &diodes)
|
||||
{
|
||||
std::vector<std::pair<const db::Region *, double> > 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<tl::Variant> &diodes)
|
||||
{
|
||||
return antenna_check2 (l2n, poly, 0, metal, 0, ratio, diodes);
|
||||
}
|
||||
|
||||
Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
|
||||
|
|
@ -609,6 +614,23 @@ Class<db::LayoutToNetlist> 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<tl::Variant> (), "[]"),
|
||||
"@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"
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
</p><p>
|
||||
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:
|
||||
</p><p>
|
||||
<pre>
|
||||
A(eff) = A + P * t
|
||||
</pre>
|
||||
</p><p>
|
||||
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:
|
||||
</p><p>
|
||||
<pre>
|
||||
errors = antenna_check(area_and_perimeter(gate, 0.5), ...)
|
||||
</pre>
|
||||
</p><p>
|
||||
"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:
|
||||
</p><p>
|
||||
<pre>
|
||||
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, ...)
|
||||
</pre>
|
||||
</p><p>
|
||||
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.
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue