From 880b9904cf9b54a2959eb19dd50c53f4a875db90 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 14 Jun 2020 22:04:16 +0200 Subject: [PATCH] WIP Netlist probing will deliver an instantiation path now. --- src/db/db/dbLayoutToNetlist.cc | 47 +++++++++---- src/db/db/dbLayoutToNetlist.h | 7 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 11 ++- src/db/db/gsiDeclDbNetlist.cc | 15 +++++ .../gsiDeclLayNetlistBrowserDialog.cc | 67 +++++++++++++++++++ src/laybasic/laybasic/laybasic.pro | 3 +- src/pya/pya/pyaMarshal.cc | 13 ++++ src/rba/rba/rbaMarshal.cc | 2 +- testdata/python/dbLayoutToNetlist.py | 54 +++++++++------ testdata/ruby/dbLayoutToNetlist.rb | 45 ++++++++----- 10 files changed, 208 insertions(+), 56 deletions(-) create mode 100644 src/laybasic/laybasic/gsiDeclLayNetlistBrowserDialog.cc diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 9e2d164d5..90d229f1f 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -1041,9 +1041,9 @@ LayoutToNetlist::build_nets (const std::vector *nets, const db: } } -db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point) +db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point, std::vector *sc_path_out) { - return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point); + return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point, sc_path_out); } size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path) @@ -1077,7 +1077,7 @@ size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell return 0; } -db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point) +db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point, std::vector *sc_path_out) { if (! m_netlist_extracted) { throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); @@ -1142,38 +1142,57 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin } + std::vector sc_path; + + db::Net *topmost_net = net; + // follow the path up in the net hierarchy using the transformation and the upper cell index as the // guide line - while (! inst_path.empty () && net->pin_count () > 0) { + while (circuit && ! inst_path.empty ()) { cell_indexes.pop_back (); - const db::Pin *pin = circuit->pin_by_id (net->begin_pins ()->pin_id ()); - tl_assert (pin != 0); + const db::Pin *pin = 0; + if (net && net->pin_count () > 0) { + pin = circuit->pin_by_id (net->begin_pins ()->pin_id ()); + tl_assert (pin != 0); + } db::DCplxTrans dtrans = dbu_trans * inst_path.back ().complex_trans () * dbu_trans_inv; // try to find a parent circuit which connects to this net db::Circuit *upper_circuit = 0; + db::SubCircuit *subcircuit = 0; db::Net *upper_net = 0; - for (db::Circuit::refs_iterator r = circuit->begin_refs (); r != circuit->end_refs () && ! upper_net; ++r) { + for (db::Circuit::refs_iterator r = circuit->begin_refs (); r != circuit->end_refs () && ! upper_circuit; ++r) { if (r->trans ().equal (dtrans) && r->circuit () && r->circuit ()->cell_index () == cell_indexes.back ()) { - upper_net = r->net_for_pin (pin->id ()); - upper_circuit = r->circuit (); + subcircuit = r.operator-> (); + if (pin) { + upper_net = subcircuit->net_for_pin (pin->id ()); + } + upper_circuit = subcircuit->circuit (); } } + net = upper_net; + if (upper_net) { - circuit = upper_circuit; - net = upper_net; - inst_path.pop_back (); + topmost_net = upper_net; } else { - break; + sc_path.push_back (subcircuit); } + circuit = upper_circuit; + inst_path.pop_back (); + } - return net; + if (sc_path_out) { + std::reverse (sc_path.begin (), sc_path.end ()); + *sc_path_out = sc_path; + } + + return topmost_net; } else { return 0; diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 63bf835aa..8b42b99f2 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -709,8 +709,11 @@ public: * * This variant accepts a micrometer-unit location. The location is given in the * coordinate space of the initial cell. + * + * The subcircuit path leading to the topmost net is stored in *sc_path_out if this + * pointer is non-null. */ - db::Net *probe_net (const db::Region &of_region, const db::DPoint &point); + db::Net *probe_net (const db::Region &of_region, const db::DPoint &point, std::vector *sc_path_out = 0); /** * @brief Finds the net by probing a specific location on the given layer @@ -718,7 +721,7 @@ public: * This variant accepts a database-unit location. The location is given in the * coordinate space of the initial cell. */ - db::Net *probe_net (const db::Region &of_region, const db::Point &point); + db::Net *probe_net (const db::Region &of_region, const db::Point &point, std::vector *sc_path_out = 0); /** * @brief Runs an antenna check on the extracted clusters diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 1e5cce32d..703699536 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -543,7 +543,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method_ext ("build_nets", &build_nets, gsi::arg ("nets"), gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::LayoutToNetlist::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), "@brief Like \\build_all_nets, but with the ability to select some nets." ) + - gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &, std::vector *)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), gsi::arg ("sc_path_out", (std::vector *) 0, "nil"), "@brief Finds the net by probing a specific location on the given layer\n" "\n" "This method will find a net looking at the given layer at the specific position.\n" @@ -557,14 +557,21 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "Optimization functions such as \\Netlist#purge will remove parts of the net which means\n" "shape to net probing may no longer work for these nets.\n" "\n" + "If non-null and an array, 'sc_path_out' will receive a list of \\SubCircuits objects which lead to the " + "net from the top circuit of the database.\n" + "\n" "This variant accepts a micrometer-unit location. The location is given in the\n" "coordinate space of the initial cell.\n" + "\n" + "The \\sc_path_out parameter has been added in version 0.27.\n" ) + - gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::Point &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::Point &, std::vector *)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), gsi::arg ("sc_path_out", (std::vector *) 0, "nil"), "@brief Finds the net by probing a specific location on the given layer\n" "See the description of the other \\probe_net variant.\n" "This variant accepts a database-unit location. The location is given in the\n" "coordinate space of the initial cell.\n" + "\n" + "The \\sc_path_out parameter has been added in version 0.27.\n" ) + gsi::method ("write|write_l2n", &db::LayoutToNetlist::save, gsi::arg ("path"), gsi::arg ("short_format", false), "@brief Writes the extracted netlist to a file.\n" diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index b8b7636c9..77cd998a6 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -466,6 +466,21 @@ Class decl_dbSubCircuit (decl_dbNetlistObject, "db", "SubCircuit gsi::method_ext ("disconnect_pin", &gsi::subcircuit_disconnect_pin1, gsi::arg ("pin"), "@brief Disconnects the given pin from any net.\n" "This version takes a \\Pin reference instead of a pin ID." + ) + + gsi::method ("trans", &db::SubCircuit::trans, + "@brief Gets the physical transformation for the subcircuit.\n" + "\n" + "This property applies to subcircuits derived from a layout. It specifies the " + "placement of the respective cell.\n" + "\n" + "This property has been introduced in version 0.27." + ) + + gsi::method ("trans=", &db::SubCircuit::set_trans, gsi::arg ("trans"), + "@brief Sets the physical transformation for the subcircuit.\n" + "\n" + "See \\trans for details about this property.\n" + "\n" + "This property has been introduced in version 0.27." ), "@brief A subcircuit inside a circuit.\n" "Circuits may instantiate other circuits as subcircuits similar to cells " diff --git a/src/laybasic/laybasic/gsiDeclLayNetlistBrowserDialog.cc b/src/laybasic/laybasic/gsiDeclLayNetlistBrowserDialog.cc new file mode 100644 index 000000000..14346efe4 --- /dev/null +++ b/src/laybasic/laybasic/gsiDeclLayNetlistBrowserDialog.cc @@ -0,0 +1,67 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "gsiDecl.h" +#include "gsiDeclBasic.h" +#include "layNetlistBrowserDialog.h" +#include "layLayoutView.h" + +namespace tl +{ + +// disable copy and default constructor for NetlistBrowserDialog +template <> struct type_traits : public type_traits +{ + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + +namespace gsi +{ + +Class decl_NetlistBrowserDialog ("lay", "NetlistBrowserDialog", + gsi::Methods (), + "@brief ..." +); + +static lay::NetlistBrowserDialog *netlist_browser (lay::LayoutView *lv) +{ + return lv->get_plugin (); +} + +// extend lay::LayoutView with the getter for the netlist browser +static +gsi::ClassExt decl_ext_layout_view ( + gsi::method_ext ("netlist_browser", &netlist_browser, + "@brief Gets the netlist browser object for the given layout view\n" + "\n" + "\nThis method has been added in version 0.27.\n" + ) +); + + + +} + diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index 7a7c8a92e..2e3e2b4c3 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -183,7 +183,8 @@ SOURCES = \ layGenericSyntaxHighlighter.cc \ layDispatcher.cc \ laySelectCellViewForm.cc \ - layLayoutStatisticsForm.cc + layLayoutStatisticsForm.cc \ + gsiDeclLayNetlistBrowserDialog.cc HEADERS = \ gtf.h \ diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index 029970e34..0cf4736b8 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -935,6 +935,12 @@ struct test_arg_func { void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) { + if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) { + // for ptr or cptr, null is an allowed value + *ret = true; + return; + } + if (! PyTuple_Check (arg) && ! PyList_Check (arg)) { *ret = false; return; @@ -971,6 +977,12 @@ struct test_arg_func { void operator () (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) { + if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) { + // for ptr or cptr, null is an allowed value + *ret = true; + return; + } + if (! PyDict_Check (arg)) { *ret = false; return; @@ -999,6 +1011,7 @@ struct test_arg_func } } }; + template <> struct test_arg_func { diff --git a/src/rba/rba/rbaMarshal.cc b/src/rba/rba/rbaMarshal.cc index 45963e4ac..631e58f63 100644 --- a/src/rba/rba/rbaMarshal.cc +++ b/src/rba/rba/rbaMarshal.cc @@ -956,7 +956,7 @@ struct test_arg_func if ((atype.is_cptr () || atype.is_ptr ()) && arg == Qnil) { // for pointers to vectors, nil is a valid value *ret = true; - } if (TYPE (arg) != T_ARRAY) { + } else if (TYPE (arg) != T_ARRAY) { *ret = false; } else { diff --git a/testdata/python/dbLayoutToNetlist.py b/testdata/python/dbLayoutToNetlist.py index 9532591cb..dc4095f19 100644 --- a/testdata/python/dbLayoutToNetlist.py +++ b/testdata/python/dbLayoutToNetlist.py @@ -69,7 +69,7 @@ class DBLayoutToNetlistTests(unittest.TestCase): ut_testsrc = os.getenv("TESTSRC") ly = pya.Layout() - ly.read(os.path.join(ut_testsrc, "testdata", "algo", "device_extract_l1.gds")) + ly.read(os.path.join(ut_testsrc, "testdata", "algo", "device_extract_l1_with_inv_nodes.gds")) l2n = pya.LayoutToNetlist(pya.RecursiveShapeIterator(ly, ly.top_cell(), [])) @@ -97,40 +97,56 @@ class DBLayoutToNetlistTests(unittest.TestCase): self.assertEqual(str(l2n.netlist()), """circuit TRANS ($1=$1,$2=$2); end; -circuit INV2 (OUT=OUT,$2=$2,$3=$3,$4=$4); +circuit INV2 (OUT=OUT,$2=$3,$3=$4); subcircuit TRANS $1 ($1=$4,$2=OUT); subcircuit TRANS $2 ($1=$3,$2=OUT); subcircuit TRANS $3 ($1=$2,$2=$4); subcircuit TRANS $4 ($1=$2,$2=$3); end; circuit RINGO (); - subcircuit INV2 $1 (OUT=OSC,$2=FB,$3=VSS,$4=VDD); - subcircuit INV2 $2 (OUT=$I29,$2=$I20,$3=VSS,$4=VDD); - subcircuit INV2 $3 (OUT=$I28,$2=$I19,$3=VSS,$4=VDD); - subcircuit INV2 $4 (OUT=$I30,$2=$I21,$3=VSS,$4=VDD); - subcircuit INV2 $5 (OUT=$I31,$2=$I22,$3=VSS,$4=VDD); - subcircuit INV2 $6 (OUT=$I32,$2=$I23,$3=VSS,$4=VDD); - subcircuit INV2 $7 (OUT=$I33,$2=$I24,$3=VSS,$4=VDD); - subcircuit INV2 $8 (OUT=$I34,$2=$I25,$3=VSS,$4=VDD); - subcircuit INV2 $9 (OUT=$I35,$2=$I26,$3=VSS,$4=VDD); - subcircuit INV2 $10 (OUT=$I36,$2=$I27,$3=VSS,$4=VDD); + subcircuit INV2 $1 (OUT='FB,OSC',$2=VSS,$3=VDD); + subcircuit INV2 $2 (OUT=$I20,$2=VSS,$3=VDD); + subcircuit INV2 $3 (OUT=$I19,$2=VSS,$3=VDD); + subcircuit INV2 $4 (OUT=$I21,$2=VSS,$3=VDD); + subcircuit INV2 $5 (OUT=$I22,$2=VSS,$3=VDD); + subcircuit INV2 $6 (OUT=$I23,$2=VSS,$3=VDD); + subcircuit INV2 $7 (OUT=$I24,$2=VSS,$3=VDD); + subcircuit INV2 $8 (OUT=$I25,$2=VSS,$3=VDD); + subcircuit INV2 $9 (OUT=$I26,$2=VSS,$3=VDD); + subcircuit INV2 $10 (OUT=$I27,$2=VSS,$3=VDD); end; """) - self.assertEqual(str(l2n.probe_net(rmetal2, pya.DPoint(0.0, 1.8))), "RINGO:FB") + self.assertEqual(str(l2n.probe_net(rmetal2, pya.DPoint(0.0, 1.8))), "RINGO:FB,OSC") + sc_path = [] + self.assertEqual(str(l2n.probe_net(rmetal2, pya.DPoint(0.0, 1.8), sc_path)), "RINGO:FB,OSC") + self.assertEqual(len(sc_path), 0) self.assertEqual(repr(l2n.probe_net(rmetal2, pya.DPoint(-2.0, 1.8))), "None") - n = l2n.probe_net(rmetal1, pya.Point(2600, 1000)) - self.assertEqual(str(n), "RINGO:$I20") + n = l2n.probe_net(rmetal1, pya.Point(2600, 1000), None) + self.assertEqual(str(n), "INV2:$2") + sc_path = [] + n = l2n.probe_net(rmetal1, pya.Point(2600, 1000), sc_path) + self.assertEqual(str(n), "INV2:$2") + self.assertEqual(len(sc_path), 1) + a = [] + t = pya.DCplxTrans() + for sc in sc_path: + a.append(sc.expanded_name()) + t = t * sc.trans + self.assertEqual(",".join(a), "$2") + self.assertEqual(str(t), "r0 *1 2.64,0") - self.assertEqual(str(l2n.shapes_of_net(n, rmetal1, True)), "(1660,-420;1660,2420;2020,2420;2020,-420);(1840,820;1840,1180;3220,1180;3220,820);(1660,2420;1660,3180;2020,3180;2020,2420);(1660,-380;1660,380;2020,380;2020,-380)") + self.assertEqual(str(l2n.shapes_of_net(n, rmetal1, True)), + "(-980,-420;-980,2420;-620,2420;-620,-420);(-800,820;-800,1180;580,1180;580,820);(-980,2420;-980,3180;-620,3180;-620,2420);(-980,-380;-980,380;-620,380;-620,-380)") shapes = pya.Shapes() l2n.shapes_of_net(n, rmetal1, True, shapes) r = pya.Region() - for s in shapes.each(): - r.insert(s.polygon) - self.assertEqual(str(r), "(1660,-420;1660,2420;2020,2420;2020,-420);(1840,820;1840,1180;3220,1180;3220,820);(1660,2420;1660,3180;2020,3180;2020,2420);(1660,-380;1660,380;2020,380;2020,-380)") + for s in shapes.each(): + r.insert(s.polygon) + self.assertEqual(str(r), + "(-980,-420;-980,2420;-620,2420;-620,-420);(-800,820;-800,1180;580,1180;580,820);(-980,2420;-980,3180;-620,3180;-620,2420);(-980,-380;-980,380;-620,380;-620,-380)") def test_10_LayoutToNetlistExtractionWithoutDevices(self): diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index 094eef598..9e61b84a9 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -104,7 +104,7 @@ class DBLayoutToNetlist_TestClass < TestBase def test_2_ShapesFromNet ly = RBA::Layout::new - ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1.gds")) + ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1_with_inv_nodes.gds")) l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, [])) @@ -133,39 +133,50 @@ class DBLayoutToNetlist_TestClass < TestBase assert_equal(l2n.netlist.to_s, <