mirror of https://github.com/KLayout/klayout.git
GSI binding of antenna check function + tests.
This commit is contained in:
parent
8d3b94201e
commit
261fb027fd
|
|
@ -862,7 +862,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<db::Region *, double> > &diodes)
|
||||
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)
|
||||
{
|
||||
// TODO: that's basically too much .. we only need the clusters
|
||||
if (! m_netlist_extracted) {
|
||||
|
|
@ -898,7 +898,7 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Reg
|
|||
double r = ratio;
|
||||
bool skip = false;
|
||||
|
||||
for (std::vector<std::pair<db::Region *, double> >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
|
||||
for (std::vector<std::pair<const db::Region *, double> >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
|
||||
|
||||
db::Region rdiode;
|
||||
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode);
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ 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<db::Region *, double> > &diodes = std::vector<std::pair<db::Region *, double> > ());
|
||||
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> > ());
|
||||
|
||||
private:
|
||||
// no copying
|
||||
|
|
|
|||
|
|
@ -1426,9 +1426,10 @@ public:
|
|||
*
|
||||
* This method will turn any region into a flat shape collection.
|
||||
*/
|
||||
void flatten ()
|
||||
db::Region &flatten ()
|
||||
{
|
||||
flat_region ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "dbLayoutToNetlistWriter.h"
|
||||
#include "dbLayoutToNetlistReader.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlVariant.h"
|
||||
|
||||
namespace gsi
|
||||
{
|
||||
|
|
@ -104,6 +105,38 @@ 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)
|
||||
{
|
||||
std::vector<std::pair<const db::Region *, double> > diode_pairs;
|
||||
|
||||
for (std::vector<tl::Variant>::const_iterator d = diodes.begin (); d != diodes.end (); ++d) {
|
||||
|
||||
if (d->is_user<db::Region> ()) {
|
||||
|
||||
diode_pairs.push_back (std::make_pair (& d->to_user<db::Region> (), 0.0));
|
||||
|
||||
} else if (d->is_list ()) {
|
||||
|
||||
const std::vector<tl::Variant> &list = d->get_list ();
|
||||
if (list.size () != 2) {
|
||||
throw tl::Exception (tl::to_string (tr ("Diode layer specifications of 'antenna' method require list of diode layer/ratio pairs (e.g. '[ [ diode_layer, 10.0 ], ... ]')")));
|
||||
}
|
||||
if (! list [0].is_user<db::Region> ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Diode layer specifications of 'antenna' method require list of diode layer/ratio pairs (e.g. '[ [ diode_layer, 10.0 ], ... ]') - first element isn't a Region object")));
|
||||
}
|
||||
if (! list [1].can_convert_to_double ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Diode layer specifications of 'antenna' method require list of diode layer/ratio pairs (e.g. '[ [ diode_layer, 10.0 ], ... ]') - second element isn't a number")));
|
||||
}
|
||||
|
||||
diode_pairs.push_back (std::make_pair (& list [0].to_user<db::Region> (), list [1].to_double ()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return l2n->antenna_check (poly, metal, ratio, diode_pairs);
|
||||
}
|
||||
|
||||
Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
|
||||
gsi::constructor ("new", &make_l2n, gsi::arg ("iter"),
|
||||
"@brief Creates a new extractor connected to an original layout\n"
|
||||
|
|
@ -386,6 +419,47 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
|
|||
gsi::method_ext ("read", &read_l2n, gsi::arg ("path"),
|
||||
"@brief Reads the extracted netlist from the file.\n"
|
||||
"This method employs the native format of KLayout.\n"
|
||||
) +
|
||||
gsi::method_ext ("antenna_check", &antenna_check, gsi::arg ("gate"), gsi::arg ("metal"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> ()),
|
||||
"@brief Runs an antenna check on the extracted clusters\n"
|
||||
"\n"
|
||||
"The antenna check will traverse all clusters and run an antenna check\n"
|
||||
"for all root clusters. The antenna ratio is defined by the total\n"
|
||||
"area of all \"metal\" shapes divided by the total area of all \"gate\" shapes\n"
|
||||
"on the cluster. Of all clusters where the antenna ratio is larger than\n"
|
||||
"the limit ratio all metal shapes are copied to the output region as\n"
|
||||
"error markers.\n"
|
||||
"\n"
|
||||
"The simple call is:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"l2n = ... # a LayoutToNetlist object\n"
|
||||
"l2n.extract_netlist\n"
|
||||
"# check for antenna ratio 10.0 of metal vs. poly:\n"
|
||||
"errors = l2n.antenna(poly, metal, 10.0)\n"
|
||||
"@endcode\n"
|
||||
"\n"
|
||||
"You can include diodes which rectify the antenna effect. "
|
||||
"Provide recognition layers for theses diodes and include them "
|
||||
"in the connections. Then specify the diode layers in the antenna call:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"...\n"
|
||||
"# include diode_layer1:\n"
|
||||
"errors = l2n.antenna(poly, metal, 10.0, [ diode_layer1 ])\n"
|
||||
"# include diode_layer1 and diode_layer2:"
|
||||
"errors = l2n.antenna(poly, metal, 10.0, [ diode_layer1, diode_layer2 ])\n"
|
||||
"@endcode\n"
|
||||
"\n"
|
||||
"Diodes can be configured to partially reduce the antenna effect depending "
|
||||
"on their area. This will make the diode_layer1 increase the ratio by 50.0 "
|
||||
"per square micrometer area of the diode:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"...\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"
|
||||
"@endcode\n"
|
||||
),
|
||||
"@brief A generic framework for extracting netlists from layouts\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -2280,6 +2280,8 @@ Class<db::Region> decl_Region ("db", "Region",
|
|||
"If the region is already flat (i.e. \\has_valid_polygons? returns true), this method will "
|
||||
"not change it.\n"
|
||||
"\n"
|
||||
"Returns 'self', so this method can be used in a dot concatenation.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.26."
|
||||
) +
|
||||
method ("has_valid_polygons?", &db::Region::has_valid_polygons,
|
||||
|
|
|
|||
|
|
@ -2390,7 +2390,7 @@ TEST(10_Antenna)
|
|||
|
||||
l2n.extract_netlist ();
|
||||
|
||||
std::vector<std::pair<db::Region *, double> > diodes;
|
||||
std::vector<std::pair<const db::Region *, double> > diodes;
|
||||
// 8.0 means: increase r by 8.0 for each um^2 of diode attached to a net
|
||||
diodes.push_back (std::make_pair (rdiode.get (), 8.0));
|
||||
|
||||
|
|
@ -2423,7 +2423,7 @@ TEST(10_Antenna)
|
|||
|
||||
l2n.extract_netlist ();
|
||||
|
||||
std::vector<std::pair<db::Region *, double> > diodes;
|
||||
std::vector<std::pair<const db::Region *, double> > diodes;
|
||||
// 0.0 means: skip all nets where there is a rdiode attached
|
||||
diodes.push_back (std::make_pair (rdiode.get (), 0.0));
|
||||
|
||||
|
|
|
|||
|
|
@ -445,6 +445,201 @@ END
|
|||
|
||||
end
|
||||
|
||||
def test_20_Antenna
|
||||
|
||||
# --- simple antenna check
|
||||
|
||||
input = File.join($ut_testsrc, "testdata", "algo", "antenna_l1.gds")
|
||||
ly = RBA::Layout::new
|
||||
ly.read(input)
|
||||
|
||||
au = File.join($ut_testsrc, "testdata", "algo", "antenna_au1.gds")
|
||||
ly_au = RBA::Layout::new
|
||||
ly_au.read(au)
|
||||
|
||||
dss = RBA::DeepShapeStore::new
|
||||
assert_equal(dss.is_singular?, false)
|
||||
|
||||
rdiode = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(1, 0)), dss)
|
||||
rpoly = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(6, 0)), dss)
|
||||
rcont = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(8, 0)), dss)
|
||||
rmetal1 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(9, 0)), dss)
|
||||
rvia1 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(11, 0)), dss)
|
||||
rmetal2 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(12, 0)), dss)
|
||||
assert_equal(dss.is_singular?, true)
|
||||
|
||||
l2n = RBA::LayoutToNetlist::new(dss)
|
||||
|
||||
l2n.register(rdiode, "diode")
|
||||
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(rpoly, rcont)
|
||||
l2n.connect(rcont, rmetal1)
|
||||
|
||||
l2n.extract_netlist
|
||||
|
||||
a1_3 = l2n.antenna_check(rpoly, rmetal1, 3)
|
||||
a1_10 = l2n.antenna_check(rpoly, rmetal1, 10)
|
||||
a1_30 = l2n.antenna_check(rpoly, rmetal1, 30)
|
||||
|
||||
# Note: flatten.merged performs some normalization
|
||||
assert_equal((a1_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(100, 0)))).to_s, "")
|
||||
assert_equal((a1_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(101, 0)))).to_s, "")
|
||||
assert_equal((a1_30.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(102, 0)))).to_s, "")
|
||||
|
||||
# --- same with flat
|
||||
|
||||
l2n._destroy
|
||||
|
||||
input = File.join($ut_testsrc, "testdata", "algo", "antenna_l1.gds")
|
||||
ly = RBA::Layout::new
|
||||
ly.read(input)
|
||||
|
||||
au = File.join($ut_testsrc, "testdata", "algo", "antenna_au1.gds")
|
||||
ly_au = RBA::Layout::new
|
||||
ly_au.read(au)
|
||||
|
||||
rfdiode = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(1, 0)))
|
||||
rfpoly = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(6, 0)))
|
||||
rfcont = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(8, 0)))
|
||||
rfmetal1 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(9, 0)))
|
||||
rfvia1 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(11, 0)))
|
||||
rfmetal2 = RBA::Region::new(ly.top_cell.begin_shapes_rec(ly.layer(12, 0)))
|
||||
assert_equal(rfdiode.is_deep?, false)
|
||||
assert_equal(rfpoly.is_deep?, false)
|
||||
assert_equal(rfmetal1.is_deep?, false)
|
||||
assert_equal(rfvia1.is_deep?, false)
|
||||
assert_equal(rfmetal2.is_deep?, false)
|
||||
|
||||
l2n = RBA::LayoutToNetlist::new(ly.top_cell.name, ly.dbu)
|
||||
|
||||
l2n.register(rfdiode, "diode")
|
||||
l2n.register(rfpoly, "poly")
|
||||
l2n.register(rfcont, "cont")
|
||||
l2n.register(rfmetal1, "metal1")
|
||||
l2n.register(rfvia1, "via1")
|
||||
l2n.register(rfmetal2, "metal2")
|
||||
|
||||
l2n.connect(rfpoly)
|
||||
l2n.connect(rfcont)
|
||||
l2n.connect(rfmetal1)
|
||||
l2n.connect(rfpoly, rfcont)
|
||||
l2n.connect(rfcont, rfmetal1)
|
||||
|
||||
l2n.extract_netlist
|
||||
|
||||
a1_3 = l2n.antenna_check(rfpoly, rfmetal1, 3)
|
||||
a1_10 = l2n.antenna_check(rfpoly, rfmetal1, 10)
|
||||
a1_30 = l2n.antenna_check(rfpoly, rfmetal1, 30)
|
||||
|
||||
# Note: flatten.merged performs some normalization
|
||||
assert_equal((a1_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(100, 0)))).to_s, "")
|
||||
assert_equal((a1_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(101, 0)))).to_s, "")
|
||||
assert_equal((a1_30.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(102, 0)))).to_s, "")
|
||||
|
||||
# --- simple antenna check with metal2
|
||||
|
||||
l2n._destroy
|
||||
l2n = RBA::LayoutToNetlist::new(dss)
|
||||
|
||||
l2n.register(rdiode, "diode")
|
||||
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(rmetal2)
|
||||
l2n.connect(rpoly, rcont)
|
||||
l2n.connect(rcont, rmetal1)
|
||||
l2n.connect(rmetal1, rvia1)
|
||||
l2n.connect(rvia1, rmetal2)
|
||||
|
||||
l2n.extract_netlist
|
||||
|
||||
a2_5 = l2n.antenna_check(rpoly, rmetal2, 5)
|
||||
a2_10 = l2n.antenna_check(rpoly, rmetal2, 10)
|
||||
a2_17 = l2n.antenna_check(rpoly, rmetal2, 17)
|
||||
|
||||
# Note: flatten.merged performs some normalization
|
||||
assert_equal((a2_5.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(200, 0)))).to_s, "")
|
||||
assert_equal((a2_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(201, 0)))).to_s, "")
|
||||
assert_equal((a2_17.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(202, 0)))).to_s, "")
|
||||
|
||||
# --- antenna check with diodes and antenna effect reduction
|
||||
|
||||
l2n._destroy
|
||||
l2n = RBA::LayoutToNetlist::new(dss)
|
||||
|
||||
l2n.register(rdiode, "diode")
|
||||
l2n.register(rpoly, "poly")
|
||||
l2n.register(rcont, "cont")
|
||||
l2n.register(rmetal1, "metal1")
|
||||
l2n.register(rvia1, "via1")
|
||||
l2n.register(rmetal2, "metal2")
|
||||
|
||||
l2n.connect(rdiode)
|
||||
l2n.connect(rpoly)
|
||||
l2n.connect(rcont)
|
||||
l2n.connect(rmetal1)
|
||||
l2n.connect(rdiode, rcont)
|
||||
l2n.connect(rpoly, rcont)
|
||||
l2n.connect(rcont, rmetal1)
|
||||
|
||||
l2n.extract_netlist
|
||||
|
||||
a3_3 = l2n.antenna_check(rpoly, rmetal1, 3, [ [ rdiode, 8.0 ] ] )
|
||||
a3_10 = l2n.antenna_check(rpoly, rmetal1, 10, [ [ rdiode, 8.0 ] ])
|
||||
a3_30 = l2n.antenna_check(rpoly, rmetal1, 30, [ [ rdiode, 8.0 ] ])
|
||||
|
||||
# Note: flatten.merged performs some normalization
|
||||
assert_equal((a3_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(300, 0)))).to_s, "")
|
||||
assert_equal((a3_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(301, 0)))).to_s, "")
|
||||
assert_equal((a3_30.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(302, 0)))).to_s, "")
|
||||
|
||||
# --- antenna check with diodes
|
||||
|
||||
l2n._destroy
|
||||
l2n = RBA::LayoutToNetlist::new(dss)
|
||||
|
||||
l2n.register(rdiode, "diode")
|
||||
l2n.register(rpoly, "poly")
|
||||
l2n.register(rcont, "cont")
|
||||
l2n.register(rmetal1, "metal1")
|
||||
l2n.register(rvia1, "via1")
|
||||
l2n.register(rmetal2, "metal2")
|
||||
|
||||
l2n.connect(rdiode)
|
||||
l2n.connect(rpoly)
|
||||
l2n.connect(rcont)
|
||||
l2n.connect(rmetal1)
|
||||
l2n.connect(rdiode, rcont)
|
||||
l2n.connect(rpoly, rcont)
|
||||
l2n.connect(rcont, rmetal1)
|
||||
|
||||
l2n.extract_netlist
|
||||
|
||||
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 ])
|
||||
|
||||
# Note: flatten.merged performs some normalization
|
||||
assert_equal((a4_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(400, 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, "")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
load("test_epilogue.rb")
|
||||
|
|
|
|||
Loading…
Reference in New Issue