GSI binding of antenna check function + tests.

This commit is contained in:
Matthias Koefferlein 2019-03-02 00:38:51 +01:00
parent 8d3b94201e
commit 261fb027fd
7 changed files with 278 additions and 6 deletions

View File

@ -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);

View File

@ -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

View File

@ -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;
}
/**

View File

@ -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"

View File

@ -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,

View File

@ -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));

View File

@ -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")