diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index cc89b9326..ecfeae414 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -30,10 +30,7 @@ namespace db { -static bool is_deep (const db::Region &r) -{ - return dynamic_cast (r.delegate ()) != 0; -} +static const unsigned int singular_layout_index = 0; // the iterator provides the hierarchical selection (enabling/disabling cells etc.) @@ -414,8 +411,6 @@ unsigned int LayoutToNetlist::layer_of (const db::Region ®ion) const db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell, bool with_device_cells) { - unsigned int layout_index = 0; - std::set device_cells; if (! with_device_cells && mp_netlist.get ()) { for (db::Netlist::device_abstract_iterator i = mp_netlist->begin_device_abstracts (); i != mp_netlist->end_device_abstracts (); ++i) { @@ -423,7 +418,7 @@ db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell } } - return dss ().cell_mapping_to_original (layout_index, &layout, cell.cell_index (), &device_cells); + return dss ().cell_mapping_to_original (singular_layout_index, &layout, cell.cell_index (), &device_cells); } db::CellMapping LayoutToNetlist::const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell) @@ -867,4 +862,66 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin } } +db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector > &diodes) +{ + // TODO: that's basically too much .. we only need the clusters + if (! m_netlist_extracted) { + throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); + } + + db::Layout &ly = dss ().layout (); + double dbu = ly.dbu (); + + db::DeepLayer dl (&dss (), singular_layout_index, ly.insert_layer ()); + + for (db::Layout::bottom_up_const_iterator cid = ly.begin_bottom_up (); cid != ly.end_bottom_up (); ++cid) { + + const connected_clusters &clusters = m_net_clusters.clusters_per_cell (*cid); + if (clusters.empty ()) { + continue; + } + + for (connected_clusters::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { + + if (! clusters.is_root (*c)) { + continue; + } + + db::Region rgate, rmetal; + + deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate); + deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal); + + double agate = rgate.area () * dbu * dbu; + double ametal = rmetal.area () * dbu * dbu; + + double r = ratio; + + for (std::vector >::const_iterator d = diodes.begin (); d != diodes.end (); ++d) { + + db::Region rdiode; + deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode); + + r += rdiode.area () * dbu * dbu / d->second; + + } + + if (tl::verbosity () >= 0 /*50*/) { + tl::info << "cell [" << ly.cell_name (*cid) << "]: agate=" << tl::to_string (agate) << ", ametal=" << tl::to_string (ametal) << ", r=" << tl::sprintf ("%.12g", r); + } + + if (agate > dbu * dbu && ametal / agate > r + db::epsilon) { + db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ()); + for (db::Region::const_iterator r = rmetal.begin_merged (); ! r.at_end (); ++r) { + shapes.insert (*r); + } + } + + } + + } + + return db::Region (new db::DeepRegion (dl)); +} + } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 367cf9340..29add2865 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -501,6 +501,25 @@ public: */ db::Net *probe_net (const db::Region &of_region, const db::Point &point); + /** + * @brief Runs an antenna check on the extracted clusters + * + * The antenna check will traverse all clusters and run an antenna check + * for all root clusters. The antenna ratio is defined by the total + * area of all "metal" shapes divided by the total area of all "gate" shapes + * on the cluster. Of all clusters where the antenna ratio is larger than + * the limit ratio all metal shapes are copied to the output region as + * error markers. + * + * 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 + * A(diode) / Ared[um^2] to the ratio. A(diode) is the area of the + * diode layer per cluster. Both the diode layer and the Ared value + * are specified as pairs in "diodes". + */ + db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector > &diodes = std::vector > ()); + private: // no copying LayoutToNetlist (const db::LayoutToNetlist &other); diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 98a13b305..16594f9c7 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -2253,3 +2253,125 @@ TEST(9_FlatExtractionWithExternalDSS) ); } +TEST(10_Antenna) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int poly = define_layer (ly, lmap, 6); + unsigned int cont = define_layer (ly, lmap, 8); + unsigned int metal1 = define_layer (ly, lmap, 9); + unsigned int via1 = define_layer (ly, lmap, 11); + unsigned int metal2 = define_layer (ly, lmap, 12); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "antenna_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::DeepShapeStore dss; + + std::auto_ptr rpoly (new db::Region (db::RecursiveShapeIterator (ly, tc, poly), dss)); + std::auto_ptr rcont (new db::Region (db::RecursiveShapeIterator (ly, tc, cont), dss)); + std::auto_ptr rmetal1 (new db::Region (db::RecursiveShapeIterator (ly, tc, metal1), dss)); + std::auto_ptr rvia1 (new db::Region (db::RecursiveShapeIterator (ly, tc, via1), dss)); + std::auto_ptr rmetal2 (new db::Region (db::RecursiveShapeIterator (ly, tc, metal2), dss)); + + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOPTOP")); + + rpoly->insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (6, 0))); + rcont->insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (8, 0))); + rmetal1->insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (9, 0))); + rvia1->insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (11, 0))); + rmetal2->insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (12, 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); + /* not yet: + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + */ + // Inter-layer + l2n.connect (*rpoly, *rcont); + l2n.connect (*rcont, *rmetal1); + /* not yet: + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + */ + + l2n.extract_netlist (); + + db::Region a1_3 = l2n.antenna_check (*rpoly, *rmetal1, 3); + db::Region a1_10 = l2n.antenna_check (*rpoly, *rmetal1, 10); + db::Region a1_30 = l2n.antenna_check (*rpoly, *rmetal1, 30); + + a1_3.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (100, 0))); + a1_10.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (101, 0))); + a1_30.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (102, 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 a2_5 = l2n.antenna_check (*rpoly, *rmetal2, 5); + db::Region a2_10 = l2n.antenna_check (*rpoly, *rmetal2, 10); + db::Region a2_17 = l2n.antenna_check (*rpoly, *rmetal2, 17); + + a2_5.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (200, 0))); + a2_10.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (201, 0))); + a2_17.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (202, 0))); + } + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "antenna_au1.gds"); + + db::compare_layouts (_this, ly2, au); +} + diff --git a/testdata/algo/antenna_au1.gds b/testdata/algo/antenna_au1.gds new file mode 100644 index 000000000..c9c5cc89f Binary files /dev/null and b/testdata/algo/antenna_au1.gds differ diff --git a/testdata/algo/antenna_l1.gds b/testdata/algo/antenna_l1.gds new file mode 100644 index 000000000..ae0d8979e Binary files /dev/null and b/testdata/algo/antenna_l1.gds differ