WIP Netlist probing will deliver an instantiation path now.

This commit is contained in:
Matthias Koefferlein 2020-06-14 22:04:16 +02:00
parent b72f819d57
commit 880b9904cf
10 changed files with 208 additions and 56 deletions

View File

@ -1041,9 +1041,9 @@ LayoutToNetlist::build_nets (const std::vector<const db::Net *> *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<db::SubCircuit *> *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<db::NetShape> &test_cluster, std::vector<db::InstElement> &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<db::SubCircuit *> *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<db::SubCircuit *> 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;

View File

@ -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<SubCircuit *> *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<SubCircuit *> *sc_path_out = 0);
/**
* @brief Runs an antenna check on the extracted clusters

View File

@ -543,7 +543,7 @@ Class<db::LayoutToNetlist> 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::SubCircuit *> *)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), gsi::arg ("sc_path_out", (std::vector<db::SubCircuit *> *) 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<db::LayoutToNetlist> 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::SubCircuit *> *)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), gsi::arg ("sc_path_out", (std::vector<db::SubCircuit *> *) 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"

View File

@ -466,6 +466,21 @@ Class<db::SubCircuit> 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 "

View File

@ -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<lay::NetlistBrowserDialog> : public type_traits<void>
{
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
}
namespace gsi
{
Class<lay::NetlistBrowserDialog> decl_NetlistBrowserDialog ("lay", "NetlistBrowserDialog",
gsi::Methods (),
"@brief ..."
);
static lay::NetlistBrowserDialog *netlist_browser (lay::LayoutView *lv)
{
return lv->get_plugin<lay::NetlistBrowserDialog> ();
}
// extend lay::LayoutView with the getter for the netlist browser
static
gsi::ClassExt<lay::LayoutView> 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"
)
);
}

View File

@ -183,7 +183,8 @@ SOURCES = \
layGenericSyntaxHighlighter.cc \
layDispatcher.cc \
laySelectCellViewForm.cc \
layLayoutStatisticsForm.cc
layLayoutStatisticsForm.cc \
gsiDeclLayNetlistBrowserDialog.cc
HEADERS = \
gtf.h \

View File

@ -935,6 +935,12 @@ struct test_arg_func<gsi::VectorType>
{
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<gsi::MapType>
{
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<gsi::MapType>
}
}
};
template <>
struct test_arg_func<gsi::ObjectType>
{

View File

@ -956,7 +956,7 @@ struct test_arg_func<gsi::VectorType>
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 {

View File

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

View File

@ -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, <<END)
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;
END
assert_equal(l2n.probe_net(rmetal2, RBA::DPoint::new(0.0, 1.8)).to_s, "RINGO:FB")
assert_equal(l2n.probe_net(rmetal2, RBA::DPoint::new(0.0, 1.8)).to_s, "RINGO:FB,OSC")
sc_path = []
assert_equal(l2n.probe_net(rmetal2, RBA::DPoint::new(0.0, 1.8), sc_path).to_s, "RINGO:FB,OSC")
assert_equal(sc_path.size, 0)
assert_equal(l2n.probe_net(rmetal2, RBA::DPoint::new(-2.0, 1.8)).inspect, "nil")
n = l2n.probe_net(rmetal1, RBA::Point::new(2600, 1000))
assert_equal(n.to_s, "RINGO:$I20")
n = l2n.probe_net(rmetal1, RBA::Point::new(2600, 1000), nil)
assert_equal(n.to_s, "INV2:$2")
sc_path = []
n = l2n.probe_net(rmetal1, RBA::Point::new(2600, 1000), sc_path)
assert_equal(n.to_s, "INV2:$2")
assert_equal(sc_path.size, 1)
assert_equal(sc_path.collect(&:expanded_name).join(","), "$2")
assert_equal(sc_path.collect(&:trans).inject(&:*).to_s, "r0 *1 2.64,0")
assert_equal(l2n.shapes_of_net(n, rmetal1, true).to_s, "(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)")
assert_equal(l2n.shapes_of_net(n, rmetal1, true).to_s,
"(-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 = RBA::Shapes::new
l2n.shapes_of_net(n, rmetal1, true, shapes)
r = RBA::Region::new
shapes.each { |s| r.insert(s.polygon) }
assert_equal(r.to_s, "(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)")
assert_equal(r.to_s,
"(-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)")
end