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
|
// TODO: that's basically too much .. we only need the clusters
|
||||||
if (! m_netlist_extracted) {
|
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);
|
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal, 0);
|
||||||
|
|
||||||
double agate = rgate.area () * dbu * dbu;
|
double agate = rgate.area () * dbu * dbu;
|
||||||
|
if (fabs (gate_perimeter_factor) > 1e-6) {
|
||||||
|
agate += rgate.perimeter () * dbu * gate_perimeter_factor;
|
||||||
|
}
|
||||||
|
|
||||||
double ametal = rmetal.area () * dbu * dbu;
|
double ametal = rmetal.area () * dbu * dbu;
|
||||||
|
if (fabs (metal_perimeter_factor) > 1e-6) {
|
||||||
|
ametal += rmetal.perimeter () * dbu * metal_perimeter_factor;
|
||||||
|
}
|
||||||
|
|
||||||
double r = ratio;
|
double r = ratio;
|
||||||
bool skip = false;
|
bool skip = false;
|
||||||
|
|
|
||||||
|
|
@ -730,6 +730,14 @@ public:
|
||||||
* the limit ratio all metal shapes are copied to the output region as
|
* the limit ratio all metal shapes are copied to the output region as
|
||||||
* error markers.
|
* 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
|
* The limit ratio can be modified by the presence of connections to
|
||||||
* other layers (specifically designating diodes for charge removal).
|
* other layers (specifically designating diodes for charge removal).
|
||||||
* Each of these layers will modify the ratio by adding a value of
|
* Each of these layers will modify the ratio by adding a value of
|
||||||
|
|
@ -742,7 +750,16 @@ public:
|
||||||
* regardless of the diode's area.
|
* regardless of the diode's area.
|
||||||
* In other words: any diode will make the net safe against antenna discharge.
|
* 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
|
* @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;
|
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;
|
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",
|
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"
|
"# diode_layer1 increases the ratio by 50 per sqaure micrometer area:\n"
|
||||||
"errors = l2n.antenna(poly, metal, 10.0 [ [ diode_layer, 50.0 ] ])\n"
|
"errors = l2n.antenna(poly, metal, 10.0 [ [ diode_layer, 50.0 ] ])\n"
|
||||||
"@/code\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"
|
"@brief A generic framework for extracting netlists from layouts\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
|
||||||
|
|
@ -2609,6 +2609,71 @@ TEST(10_Antenna)
|
||||||
a4_30.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (402, 0)));
|
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 ();
|
std::string au = tl::testsrc ();
|
||||||
au = tl::combine_path (au, "testdata");
|
au = tl::combine_path (au, "testdata");
|
||||||
au = tl::combine_path (au, "algo");
|
au = tl::combine_path (au, "algo");
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,14 @@ module DRC
|
||||||
DRCAsDots::new(false)
|
DRCAsDots::new(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def area_only(r)
|
||||||
|
DRCAreaAndPerimeter::new(r, 0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def area_and_perimeter(r, f)
|
||||||
|
DRCAreaAndPerimeter::new(r, f)
|
||||||
|
end
|
||||||
|
|
||||||
# %DRC%
|
# %DRC%
|
||||||
# @brief Defines SPICE output format (with options)
|
# @brief Defines SPICE output format (with options)
|
||||||
# @name write_spice
|
# @name write_spice
|
||||||
|
|
@ -1767,6 +1775,29 @@ CODE
|
||||||
@l2ndb_index = i
|
@l2ndb_index = i
|
||||||
end
|
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
|
private
|
||||||
|
|
||||||
def _make_string(v)
|
def _make_string(v)
|
||||||
|
|
|
||||||
|
|
@ -253,12 +253,12 @@ module DRC
|
||||||
if args.size == 1
|
if args.size == 1
|
||||||
a = args[0]
|
a = args[0]
|
||||||
if a.is_a?(Range)
|
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
|
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
|
end
|
||||||
elsif args.size == 2
|
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
|
else
|
||||||
raise("Invalid number of arguments for method '#{mn}'")
|
raise("Invalid number of arguments for method '#{mn}'")
|
||||||
end
|
end
|
||||||
|
|
@ -403,12 +403,12 @@ CODE
|
||||||
if args.size == 1
|
if args.size == 1
|
||||||
a = args[0]
|
a = args[0]
|
||||||
if a.is_a?(Range)
|
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
|
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
|
end
|
||||||
elsif args.size == 2
|
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
|
else
|
||||||
raise("Invalid number of arguments for method '#{mn}'")
|
raise("Invalid number of arguments for method '#{mn}'")
|
||||||
end
|
end
|
||||||
|
|
@ -452,12 +452,12 @@ CODE
|
||||||
if args.size == 1
|
if args.size == 1
|
||||||
a = args[0]
|
a = args[0]
|
||||||
if a.is_a?(Range)
|
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
|
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
|
end
|
||||||
elsif args.size == 2
|
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
|
else
|
||||||
raise("Invalid number of arguments for method '#{mn}'")
|
raise("Invalid number of arguments for method '#{mn}'")
|
||||||
end
|
end
|
||||||
|
|
@ -562,7 +562,7 @@ CODE
|
||||||
|
|
||||||
def rounded_corners(inner, outer, n)
|
def rounded_corners(inner, outer, n)
|
||||||
requires_region("rounded_corners")
|
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
|
end
|
||||||
|
|
||||||
# %DRC%
|
# %DRC%
|
||||||
|
|
@ -579,7 +579,7 @@ CODE
|
||||||
|
|
||||||
def smoothed(d)
|
def smoothed(d)
|
||||||
requires_region("smoothed")
|
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
|
end
|
||||||
|
|
||||||
# %DRC%
|
# %DRC%
|
||||||
|
|
@ -1106,9 +1106,9 @@ CODE
|
||||||
def ongrid(*args)
|
def ongrid(*args)
|
||||||
requires_region("ongrid")
|
requires_region("ongrid")
|
||||||
if args.size == 1
|
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
|
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
|
else
|
||||||
raise("Invalid number of arguments for method 'ongrid'")
|
raise("Invalid number of arguments for method 'ongrid'")
|
||||||
end
|
end
|
||||||
|
|
@ -1143,14 +1143,14 @@ CODE
|
||||||
requires_region("#{f}")
|
requires_region("#{f}")
|
||||||
gx = gy = 0
|
gx = gy = 0
|
||||||
if args.size == 1
|
if args.size == 1
|
||||||
gx = gy = prep_value(args[0])
|
gx = gy = @engine._prep_value(args[0])
|
||||||
elsif args.size == 2
|
elsif args.size == 2
|
||||||
gx = prep_value(args[0])
|
gx = @engine._prep_value(args[0])
|
||||||
gy = prep_value(args[1])
|
gy = @engine._prep_value(args[1])
|
||||||
else
|
else
|
||||||
raise("Invalid number of arguments for method 'ongrid'")
|
raise("Invalid number of arguments for method 'ongrid'")
|
||||||
end
|
end
|
||||||
aa = args.collect { |a| prep_value(a) }
|
aa = args.collect { |a| @engine._prep_value(a) }
|
||||||
if :#{f} == :snap && @engine.is_tiled?
|
if :#{f} == :snap && @engine.is_tiled?
|
||||||
# in tiled mode, no modifying versions are available
|
# in tiled mode, no modifying versions are available
|
||||||
@data = @engine._tcmd(@data, 0, @data.class, :snapped, gx, gy)
|
@data = @engine._tcmd(@data, 0, @data.class, :snapped, gx, gy)
|
||||||
|
|
@ -2039,7 +2039,7 @@ CODE
|
||||||
eval <<"CODE"
|
eval <<"CODE"
|
||||||
def #{f}(length, fraction = 0.0)
|
def #{f}(length, fraction = 0.0)
|
||||||
requires_edges("#{f}")
|
requires_edges("#{f}")
|
||||||
length = prep_value(length)
|
length = @engine._prep_value(length)
|
||||||
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :#{f}, length, fraction))
|
DRCLayer::new(@engine, @engine._tcmd(@data, 0, RBA::Edges, :#{f}, length, fraction))
|
||||||
end
|
end
|
||||||
CODE
|
CODE
|
||||||
|
|
@ -2087,16 +2087,16 @@ CODE
|
||||||
av = [ 0, 0, 0, 0, false ]
|
av = [ 0, 0, 0, 0, false ]
|
||||||
args.each_with_index do |a,i|
|
args.each_with_index do |a,i|
|
||||||
if a.is_a?(Hash)
|
if a.is_a?(Hash)
|
||||||
a[:begin] && av[0] = prep_value(a[:begin])
|
a[:begin] && av[0] = @engine._prep_value(a[:begin])
|
||||||
a[:end] && av[1] = prep_value(a[:end])
|
a[:end] && av[1] = @engine._prep_value(a[:end])
|
||||||
a[:out] && av[2] = prep_value(a[:out])
|
a[:out] && av[2] = @engine._prep_value(a[:out])
|
||||||
a[:in] && av[3] = prep_value(a[:in])
|
a[:in] && av[3] = @engine._prep_value(a[:in])
|
||||||
a[:joined] && av[4] = true
|
a[:joined] && av[4] = true
|
||||||
elsif i < 4
|
elsif i < 4
|
||||||
if !a.is_a?(1.class) && !a.is_a?(Float)
|
if !a.is_a?(1.class) && !a.is_a?(Float)
|
||||||
raise("Invalid type for argument " + (i+1).to_s + " (method '#{f}')")
|
raise("Invalid type for argument " + (i+1).to_s + " (method '#{f}')")
|
||||||
end
|
end
|
||||||
av[i] = prep_value(a)
|
av[i] = @engine._prep_value(a)
|
||||||
elsif i == 4
|
elsif i == 4
|
||||||
if a.is_a?(DRCJoinFlag)
|
if a.is_a?(DRCJoinFlag)
|
||||||
av[i] = a.value
|
av[i] = a.value
|
||||||
|
|
@ -2142,7 +2142,7 @@ CODE
|
||||||
eval <<"CODE"
|
eval <<"CODE"
|
||||||
def #{f}(dist)
|
def #{f}(dist)
|
||||||
requires_edges("#{f}")
|
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
|
end
|
||||||
CODE
|
CODE
|
||||||
end
|
end
|
||||||
|
|
@ -2635,11 +2635,11 @@ CODE
|
||||||
elsif a.is_a?(DRCLayer)
|
elsif a.is_a?(DRCLayer)
|
||||||
other = a
|
other = a
|
||||||
elsif a.is_a?(DRCProjectionLimits)
|
elsif a.is_a?(DRCProjectionLimits)
|
||||||
minp = prep_value(a.min)
|
minp = @engine._prep_value(a.min)
|
||||||
maxp = prep_value(a.max)
|
maxp = @engine._prep_value(a.max)
|
||||||
elsif a.is_a?(Float) || a.is_a?(1.class)
|
elsif a.is_a?(Float) || a.is_a?(1.class)
|
||||||
value && raise("Value already specified")
|
value && raise("Value already specified")
|
||||||
value = prep_value(a)
|
value = @engine._prep_value(a)
|
||||||
else
|
else
|
||||||
raise("#{f}: Parameter #" + n.to_s + " does not have an expected type")
|
raise("#{f}: Parameter #" + n.to_s + " does not have an expected type")
|
||||||
end
|
end
|
||||||
|
|
@ -2694,11 +2694,11 @@ CODE
|
||||||
elsif a.is_a?(DRCLayer)
|
elsif a.is_a?(DRCLayer)
|
||||||
other = a
|
other = a
|
||||||
elsif a.is_a?(DRCProjectionLimits)
|
elsif a.is_a?(DRCProjectionLimits)
|
||||||
minp = prep_value(a.min)
|
minp = @engine._prep_value(a.min)
|
||||||
maxp = prep_value(a.max)
|
maxp = @engine._prep_value(a.max)
|
||||||
elsif a.is_a?(Float) || a.is_a?(1.class)
|
elsif a.is_a?(Float) || a.is_a?(1.class)
|
||||||
value && raise("Value already specified")
|
value && raise("Value already specified")
|
||||||
value = prep_value(a)
|
value = @engine._prep_value(a)
|
||||||
else
|
else
|
||||||
raise("#{f}: Parameter #" + n.to_s + " does not have an expected type")
|
raise("#{f}: Parameter #" + n.to_s + " does not have an expected type")
|
||||||
end
|
end
|
||||||
|
|
@ -2868,7 +2868,7 @@ CODE
|
||||||
values = []
|
values = []
|
||||||
args.each do |a|
|
args.each do |a|
|
||||||
if a.is_a?(1.class) || a.is_a?(Float)
|
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
|
v.abs > dist && dist = v.abs
|
||||||
values.push(v)
|
values.push(v)
|
||||||
elsif a.is_a?(DRCSizingMode)
|
elsif a.is_a?(DRCSizingMode)
|
||||||
|
|
@ -2918,7 +2918,7 @@ CODE
|
||||||
def polygons(*args)
|
def polygons(*args)
|
||||||
requires_edge_pairs("polygons")
|
requires_edge_pairs("polygons")
|
||||||
args.size <= 1 || raise("polygons: Method requires 0 or 1 arguments")
|
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))
|
DRCLayer::new(@engine, @engine._cmd(@data, :polygons, *aa))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -3007,7 +3007,7 @@ CODE
|
||||||
%w(extents moved transformed).each do |f|
|
%w(extents moved transformed).each do |f|
|
||||||
eval <<"CODE"
|
eval <<"CODE"
|
||||||
def #{f}(*args)
|
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))
|
DRCLayer::new(@engine, @engine._cmd(@data, :#{f}, *aa))
|
||||||
end
|
end
|
||||||
CODE
|
CODE
|
||||||
|
|
@ -3016,7 +3016,7 @@ CODE
|
||||||
%w(move transform).each do |f|
|
%w(move transform).each do |f|
|
||||||
eval <<"CODE"
|
eval <<"CODE"
|
||||||
def #{f}(*args)
|
def #{f}(*args)
|
||||||
aa = args.collect { |a| prep_value(a) }
|
aa = args.collect { |a| @engine._prep_value(a) }
|
||||||
@engine._cmd(@data, :#{f}, *aa)
|
@engine._cmd(@data, :#{f}, *aa)
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
@ -3062,13 +3062,13 @@ CODE
|
||||||
|
|
||||||
def merged(*args)
|
def merged(*args)
|
||||||
requires_edges_or_region("merged")
|
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))
|
DRCLayer::new(@engine, @engine._tcmd(@data, 0, @data.class, :merged, *aa))
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge(*args)
|
def merge(*args)
|
||||||
requires_edges_or_region("merge")
|
requires_edges_or_region("merge")
|
||||||
aa = args.collect { |a| prep_value(a) }
|
aa = args.collect { |a| @engine._prep_value(a) }
|
||||||
if @engine.is_tiled?
|
if @engine.is_tiled?
|
||||||
# in tiled mode, no modifying versions are available
|
# in tiled mode, no modifying versions are available
|
||||||
@data = @engine._tcmd(@data, 0, @data.class, :merged, *aa)
|
@data = @engine._tcmd(@data, 0, @data.class, :merged, *aa)
|
||||||
|
|
@ -3172,29 +3172,6 @@ CODE
|
||||||
end
|
end
|
||||||
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -336,16 +336,74 @@ module DRC
|
||||||
# Multiple diode specifications are allowed. Just add them
|
# Multiple diode specifications are allowed. Just add them
|
||||||
# to the antenna_check call.
|
# 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
|
# The error shapes produced by the antenna check are copies
|
||||||
# of the metal shapes on the metal layers of each network
|
# of the metal shapes on the metal layers of each network
|
||||||
# violating the antenna rule.
|
# 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)")
|
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)")
|
metal.requires_region("Netter#antenna_check (metal argument)")
|
||||||
|
|
||||||
if !ratio.is_a?(1.class) && !ratio.is_a?(Float)
|
if !ratio.is_a?(1.class) && !ratio.is_a?(Float)
|
||||||
|
|
@ -363,7 +421,7 @@ module DRC
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,5 +112,17 @@ module DRC
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,40 @@ errors = antenna_check(gate, metal1, 50.0, [ diode, 10.0 ])
|
||||||
Multiple diode specifications are allowed. Just add them
|
Multiple diode specifications are allowed. Just add them
|
||||||
to the antenna_check call.
|
to the antenna_check call.
|
||||||
</p><p>
|
</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
|
The error shapes produced by the antenna check are copies
|
||||||
of the metal shapes on the metal layers of each network
|
of the metal shapes on the metal layers of each network
|
||||||
violating the antenna rule.
|
violating the antenna rule.
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -26,6 +26,8 @@ connect(metal1, via1)
|
||||||
connect(via1, metal2)
|
connect(via1, metal2)
|
||||||
|
|
||||||
antenna_check(gate, metal2, 1.0, diode).output(101)
|
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, 5.0, diode).output(105)
|
||||||
antenna_check(gate, metal2, 10.0, diode).output(110)
|
antenna_check(gate, metal2, 10.0, diode).output(110)
|
||||||
antenna_check(gate, metal2, 50.0, diode).output(150)
|
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, 5.0, [ diode, 5.0 ]).output(205)
|
||||||
antenna_check(gate, metal2, 10.0, [ diode, 5.0 ]).output(210)
|
antenna_check(gate, metal2, 10.0, [ diode, 5.0 ]).output(210)
|
||||||
antenna_check(gate, metal2, 50.0, [ diode, 5.0 ]).output(250)
|
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
|
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_10 = l2n.antenna_check(rpoly, rmetal1, 10, [ rdiode ])
|
||||||
a4_30 = l2n.antenna_check(rpoly, rmetal1, 30, [ 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_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, "")
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue