From 89cbe930aee88a4c69613d6ab42671e705d58c1e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 May 2019 01:37:45 +0200 Subject: [PATCH] WIP: GSI binding of LVS framework, tests and debugging --- src/db/db/gsiDeclDbLayoutToNetlist.cc | 2 +- src/db/db/gsiDeclDbLayoutVsSchematic.cc | 30 +-- src/db/db/gsiDeclDbNetlist.cc | 100 ++++++++-- .../laybasic/layNetlistCrossReferenceModel.cc | 2 +- src/rba/unit_tests/rba.cc | 2 + testdata/ruby/dbLayoutVsSchematic.rb | 177 ++++++++++++++++++ testdata/ruby/dbNetlistCrossReference.rb | 167 +++++++++++++++++ 7 files changed, 446 insertions(+), 34 deletions(-) create mode 100644 testdata/ruby/dbLayoutVsSchematic.rb create mode 100644 testdata/ruby/dbNetlistCrossReference.rb diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index d419b4111..53b632a3e 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -260,7 +260,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "The name is optional. If given, the layer will already be named accordingly (see \\register).\n" ) + gsi::factory ("make_layer", (db::Region *(db::LayoutToNetlist::*) (unsigned int, const std::string &)) &db::LayoutToNetlist::make_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), - "@brief Creates a new hierarchical region representing an original layer\n" + "@brief Creates a new hierarchical region reprfesenting an original layer\n" "'layer_index' is the layer index of the desired layer in the original layout.\n" "This variant produces polygons and takes texts for net name annotation.\n" "A variant not taking texts is \\make_polygon_layer. A Variant only taking\n" diff --git a/src/db/db/gsiDeclDbLayoutVsSchematic.cc b/src/db/db/gsiDeclDbLayoutVsSchematic.cc index 1cbc034e3..9d9b60d68 100644 --- a/src/db/db/gsiDeclDbLayoutVsSchematic.cc +++ b/src/db/db/gsiDeclDbLayoutVsSchematic.cc @@ -55,12 +55,17 @@ static db::LayoutVsSchematic *make_lvs_flat (const std::string &topcell_name, do return new db::LayoutVsSchematic (topcell_name, dbu); } -static db::LayoutToNetlist *l2n_from_lvs (db::LayoutVsSchematic *lvs) +static void save_l2n (db::LayoutVsSchematic *lvs, const std::string &path, bool short_format) { - return lvs; + lvs->db::LayoutToNetlist::save (path, short_format); } -Class decl_dbLayoutVsSchematic ("db", "LayoutVsSchematic", +static void load_l2n (db::LayoutVsSchematic *lvs, const std::string &path) +{ + lvs->db::LayoutToNetlist::load (path); +} + +Class decl_dbLayoutVsSchematic (decl_dbLayoutToNetlist, "db", "LayoutVsSchematic", gsi::constructor ("new", &make_lvs, gsi::arg ("iter"), "@brief Creates a new LVS object with the extractor connected to an original layout\n" "This constructor will attach the extractor of the LVS object to an original layout through the " @@ -105,18 +110,13 @@ Class decl_dbLayoutVsSchematic ("db", "LayoutVsSchematic" "\n" "See \\NetlistCrossReference for more details.\n" ) + - gsi::method_ext ("l2n", &l2n_from_lvs, - "@brief Gets the \\LayoutToNetlist interface of this object.\n" - "This method renders the \\LayoutToNetlist part of self. It is useful to access \\read and \\write from this " - "interface instead of the versions from \\LayoutVsSchematic:\n" - "\n" - "@code\n" - "lvs = ... # an LVS object\n" - "# writes the LayoutToNetlist data:\n" - "lvs.l2n.write(\"data.l2n\")\n" - "# writes the LayoutVsSchematic data:\n" - "lvs.lvs.write(\"data.l2n\")\n" - "@/code\n" + gsi::method_ext ("write_l2n", &save_l2n, gsi::arg ("path"), gsi::arg ("short_format", false), + "@brief Writes the \\LayoutToNetlist part of the object to a file.\n" + "This method employs the native format of KLayout.\n" + ) + + gsi::method_ext ("read_l2n", &load_l2n, gsi::arg ("path"), + "@brief Reads the \\LayoutToNetlist part of the object from a file.\n" + "This method employs the native format of KLayout.\n" ) + gsi::method ("write", &db::LayoutVsSchematic::save, gsi::arg ("path"), gsi::arg ("short_format", false), "@brief Writes the LVS object to a file.\n" diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 47ec269f0..76c6ff81a 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -33,6 +33,66 @@ namespace gsi { +// ------------------------------------------------------------ +// @@@@ + +template +class ConstMethodBiIterWithTransfer + : public MethodSpecificBase +{ +public: + typedef IterAdaptorAbstractBase iter_adaptor_base_type; + typedef IterAdaptor iter_adaptor_type; + + ConstMethodBiIterWithTransfer (const std::string &name, I (X::*b) () const, I (X::*e) () const, const std::string &doc) + : MethodSpecificBase (name, doc, true, false, 0), m_b (b), m_e (e) + { + } + + ConstMethodBiIterWithTransfer *add_args () + { + return this; + } + + void initialize () + { + this->clear (); + this->template set_return (); + } + + virtual MethodBase *clone () const + { + return new ConstMethodBiIterWithTransfer (*this); + } + + virtual void call (void *cls, SerialArgs &, SerialArgs &ret) const + { + this->mark_called (); + ret.write (static_cast (new iter_adaptor_type ((((const X *)cls)->*m_b) (), (((const X *)cls)->*m_e) ()))); + } + +private: + I (X::*m_b) () const; + I (X::*m_e) () const; +}; + +template +ConstMethodBiIterWithTransfer * +_iterator_with_transfer (const std::string &name, I (X::*b) () const, I (X::*e) () const, const std::string &doc) +{ + return new ConstMethodBiIterWithTransfer (name, b, e, doc); +} + +template +Methods +iterator_with_transfer (const std::string &name, I (X::*b) () const, I (X::*e) () const, const std::string &doc = std::string ()) +{ + return Methods (_iterator_with_transfer (name, b, e, doc)); +} + +// @@@@ +// ------------------------------------------------------------ + Class decl_dbPin ("db", "Pin", gsi::method ("id", &db::Pin::id, "@brief Gets the ID of the pin.\n" @@ -265,7 +325,7 @@ Class decl_dbDevice ("db", "Device", "@hide\n" "Provided for test purposes mainly." ) + - gsi::method ("circuit", (db::Circuit *(db::Device::*) ()) &db::Device::circuit, + gsi::method ("circuit", (const db::Circuit *(db::Device::*) () const) &db::Device::circuit, "@brief Gets the circuit the device lives in." ) + gsi::method ("id", &db::Device::id, @@ -286,7 +346,7 @@ Class decl_dbDevice ("db", "Device", "@brief Gets the expanded name of the device.\n" "The expanded name takes the name of the device. If the name is empty, the numeric ID will be used to build a name. " ) + - gsi::method ("net_for_terminal", (db::Net *(db::Device::*) (size_t)) &db::Device::net_for_terminal, gsi::arg ("terminal_id"), + gsi::method ("net_for_terminal", (const db::Net *(db::Device::*) (size_t) const) &db::Device::net_for_terminal, gsi::arg ("terminal_id"), "@brief Gets the net connected to the specified terminal.\n" "If the terminal is not connected, nil is returned for the net." ) + @@ -339,7 +399,7 @@ Class decl_dbDevice ("db", "Device", ); Class decl_dbDeviceAbstract ("db", "DeviceAbstract", - gsi::method ("netlist", (db::Netlist *(db::DeviceAbstract::*) ()) &db::DeviceAbstract::netlist, + gsi::method ("netlist", (const db::Netlist *(db::DeviceAbstract::*) () const) &db::DeviceAbstract::netlist, "@brief Gets the netlist the device abstract lives in." ) + gsi::method ("device_class", &db::DeviceAbstract::device_class, @@ -388,10 +448,10 @@ static void subcircuit_disconnect_pin1 (db::SubCircuit *subcircuit, const db::Pi } Class decl_dbSubCircuit ("db", "SubCircuit", - gsi::method ("circuit_ref", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit_ref, + gsi::method ("circuit_ref", (const db::Circuit *(db::SubCircuit::*) () const) &db::SubCircuit::circuit_ref, "@brief Gets the circuit referenced by the subcircuit.\n" ) + - gsi::method ("circuit", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit, + gsi::method ("circuit", (const db::Circuit *(db::SubCircuit::*) () const) &db::SubCircuit::circuit, "@brief Gets the circuit the subcircuit lives in.\n" "This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \\circuit_ref." ) + @@ -413,7 +473,7 @@ Class decl_dbSubCircuit ("db", "SubCircuit", "@brief Gets the expanded name of the subcircuit.\n" "The expanded name takes the name of the subcircuit. If the name is empty, the numeric ID will be used to build a name. " ) + - gsi::method ("net_for_pin", (db::Net *(db::SubCircuit::*) (size_t)) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"), + gsi::method ("net_for_pin", (const db::Net *(db::SubCircuit::*) (size_t) const) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"), "@brief Gets the net connected to the specified pin of the subcircuit.\n" "If the pin is not connected, nil is returned for the net." ) + @@ -450,17 +510,17 @@ Class decl_dbNetTerminalRef ("db", "NetTerminalRef", gsi::method ("terminal_id", &db::NetTerminalRef::terminal_id, "@brief Gets the ID of the terminal of the device the connection is made to." ) + - gsi::method ("device", (db::Device *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::device, + gsi::method ("device", (const db::Device *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::device, "@brief Gets the device reference.\n" "Gets the device object that this connection is made to." ) + - gsi::method ("net", (db::Net *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::net, + gsi::method ("net", (const db::Net *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::net, "@brief Gets the net this terminal reference is attached to" ) + - gsi::method ("device_class", (db::DeviceClass *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::device_class, + gsi::method ("device_class", (const db::DeviceClass *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::device_class, "@brief Gets the class of the device which is addressed." ) + - gsi::method ("terminal_def", (db::DeviceTerminalDefinition *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::terminal_def, + gsi::method ("terminal_def", (const db::DeviceTerminalDefinition *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::terminal_def, "@brief Gets the terminal definition of the terminal that is connected" ), "@brief A connection to a terminal of a device.\n" @@ -476,7 +536,7 @@ Class decl_dbNetPinRef ("db", "NetPinRef", gsi::method ("pin", &db::NetPinRef::pin, "@brief Gets the \\Pin object of the pin the connection is made to." ) + - gsi::method ("net", (db::Net *(db::NetPinRef::*) ()) &db::NetPinRef::net, + gsi::method ("net", (const db::Net *(db::NetPinRef::*) () const) &db::NetPinRef::net, "@brief Gets the net this pin reference is attached to" ), "@brief A connection to an outgoing pin of the circuit.\n" @@ -492,12 +552,12 @@ Class decl_dbNetSubcircuitPinRef ("db", "NetSubcircuitP gsi::method ("pin", &db::NetSubcircuitPinRef::pin, "@brief Gets the \\Pin object of the pin the connection is made to." ) + - gsi::method ("subcircuit", (db::SubCircuit *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::subcircuit, + gsi::method ("subcircuit", (const db::SubCircuit *(db::NetSubcircuitPinRef::*) () const) &db::NetSubcircuitPinRef::subcircuit, "@brief Gets the subcircuit reference.\n" "This attribute indicates the subcircuit the net attaches to. The " "subcircuit lives in the same circuit than the net. " ) + - gsi::method ("net", (db::Net *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::net, + gsi::method ("net", (const db::Net *(db::NetSubcircuitPinRef::*) () const) &db::NetSubcircuitPinRef::net, "@brief Gets the net this pin reference is attached to" ), "@brief A connection to a pin of a subcircuit.\n" @@ -540,21 +600,27 @@ Class decl_dbNet ("db", "Net", "@brief Gets the cluster ID of the net.\n" "See \\cluster_id= for details about the cluster ID." ) + - gsi::iterator ("each_pin", (db::Net::pin_iterator (db::Net::*) ()) &db::Net::begin_pins, (db::Net::pin_iterator (db::Net::*) ()) &db::Net::end_pins, + // @@@ + gsi::iterator_with_transfer ("each_pin", (db::Net::const_pin_iterator (db::Net::*) () const) &db::Net::begin_pins, (db::Net::const_pin_iterator (db::Net::*) () const) &db::Net::end_pins, "@brief Iterates over all outgoing pins the net connects.\n" "Pin connections are described by \\NetPinRef objects. Pin connections " "are connections to outgoing pins of the circuit the net lives in." ) + - gsi::iterator ("each_subcircuit_pin", (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::begin_subcircuit_pins, (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::end_subcircuit_pins, + // @@@ + // @@@ + gsi::iterator_with_transfer ("each_subcircuit_pin", (db::Net::const_subcircuit_pin_iterator (db::Net::*) () const) &db::Net::begin_subcircuit_pins, (db::Net::const_subcircuit_pin_iterator (db::Net::*) () const) &db::Net::end_subcircuit_pins, "@brief Iterates over all subcircuit pins the net connects.\n" "Subcircuit pin connections are described by \\NetSubcircuitPinRef objects. These are " "connections to specific pins of subcircuits." ) + - gsi::iterator ("each_terminal", (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::begin_terminals, (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::end_terminals, + // @@@ + // @@@ + gsi::iterator_with_transfer ("each_terminal", (db::Net::const_terminal_iterator (db::Net::*) () const) &db::Net::begin_terminals, (db::Net::const_terminal_iterator (db::Net::*) () const) &db::Net::end_terminals, "@brief Iterates over all terminals the net connects.\n" "Terminals connect devices. Terminal connections are described by \\NetTerminalRef " "objects." ) + + // @@@ gsi::method ("is_floating?", &db::Net::is_floating, "@brief Returns true, if the net is floating.\n" "Floating nets are those who don't have any or only a single connection (pin_count + terminal_count < 2)." @@ -1032,7 +1098,7 @@ static db::SubCircuit *create_subcircuit1 (db::Circuit *c, db::Circuit *cc, cons return sc; } -static db::Net *circuit_net_for_pin (db::Circuit *c, const db::Pin *pin) +static const db::Net *circuit_net_for_pin (const db::Circuit *c, const db::Pin *pin) { return pin ? c->net_for_pin (pin->id ()) : 0; } diff --git a/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc b/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc index dc2cb742c..041f3e0c5 100644 --- a/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc +++ b/src/laybasic/laybasic/layNetlistCrossReferenceModel.cc @@ -34,7 +34,7 @@ NetlistCrossReferenceModel::NetlistCrossReferenceModel (const db::NetlistCrossRe size_t NetlistCrossReferenceModel::circuit_count () const { - return mp_cross_ref->circuit_count (); + return mp_cross_ref.get () ? mp_cross_ref->circuit_count () : 0; } size_t NetlistCrossReferenceModel::net_count (const circuit_pair &circuits) const diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index cdc31958b..2e81adf23 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -109,6 +109,8 @@ RUBYTEST (dbLayoutTest, "dbLayoutTest.rb") RUBYTEST (dbLayoutDiff, "dbLayoutDiff.rb") RUBYTEST (dbLayoutQuery, "dbLayoutQuery.rb") RUBYTEST (dbLayoutToNetlist, "dbLayoutToNetlist.rb") +RUBYTEST (dbLayoutVsSchematic, "dbLayoutVsSchematic.rb") +RUBYTEST (dbNetlistCrossReference, "dbNetlistCrossReference.rb") RUBYTEST (dbMatrix, "dbMatrix.rb") RUBYTEST (dbNetlist, "dbNetlist.rb") RUBYTEST (dbNetlistDeviceClasses, "dbNetlistDeviceClasses.rb") diff --git a/testdata/ruby/dbLayoutVsSchematic.rb b/testdata/ruby/dbLayoutVsSchematic.rb new file mode 100644 index 000000000..5204457c2 --- /dev/null +++ b/testdata/ruby/dbLayoutVsSchematic.rb @@ -0,0 +1,177 @@ + +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2019 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 + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBLayoutVsSchematic_TestClass < TestBase + + def test_1_Basic + + lvs = RBA::LayoutVsSchematic::new + + assert_equal(lvs.xref == nil, true) + assert_equal(lvs.reference == nil, true) + + end + + def test_2_Flow + + ly = RBA::Layout::new + ly.read(File.join($ut_testsrc, "testdata", "algo", "lvs_test_1.gds")) + + lvs = RBA::LayoutVsSchematic::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, [])) + + nwell = lvs.make_layer(ly.layer(1, 0), "nwell") + active = lvs.make_layer(ly.layer(2, 0), "active") + pplus = lvs.make_layer(ly.layer(10, 0), "pplus") + nplus = lvs.make_layer(ly.layer(11, 0), "nplus") + poly = lvs.make_layer(ly.layer(3, 0), "poly") + poly_lbl = lvs.make_text_layer(ly.layer(3, 1), "poly_lbl") + diff_cont = lvs.make_layer(ly.layer(4, 0), "diff_cont") + poly_cont = lvs.make_layer(ly.layer(5, 0), "poly_cont") + metal1 = lvs.make_layer(ly.layer(6, 0), "metal1") + metal1_lbl = lvs.make_text_layer(ly.layer(6, 1), "metal1_lbl") + via1 = lvs.make_layer(ly.layer(7, 0), "via1") + metal2 = lvs.make_layer(ly.layer(8, 0), "metal2") + metal2_lbl = lvs.make_text_layer(ly.layer(8, 1), "metal2_lbl") + bulk = lvs.make_layer + + # compute some layers + active_in_nwell = active & nwell + pactive = active_in_nwell & pplus + ntie = active_in_nwell & nplus + pgate = pactive & poly + psd = pactive - pgate + + active_outside_nwell = active - nwell + nactive = active_outside_nwell & nplus + ptie = active_outside_nwell & pplus + ngate = nactive & poly + nsd = nactive - ngate + + # device extraction + pmos_ex = RBA::DeviceExtractorMOS4Transistor::new("PMOS") + dl = { "SD" => psd, "G" => pgate, "P" => poly, "W" => nwell } + lvs.extract_devices(pmos_ex, dl) + + nmos_ex = RBA::DeviceExtractorMOS4Transistor::new("NMOS") + dl = { "SD" => nsd, "G" => ngate, "P" => poly, "W" => bulk } + lvs.extract_devices(nmos_ex, dl) + + # register derived layers for connectivity + lvs.register(psd, "psd") + lvs.register(nsd, "nsd") + lvs.register(ptie, "ptie") + lvs.register(ntie, "ntie") + + # intra-layer + lvs.connect(psd) + lvs.connect(nsd) + lvs.connect(nwell) + lvs.connect(poly) + lvs.connect(diff_cont) + lvs.connect(poly_cont) + lvs.connect(metal1) + lvs.connect(via1) + lvs.connect(metal2) + lvs.connect(ptie) + lvs.connect(ntie) + + # inter-layer + lvs.connect(psd, diff_cont) + lvs.connect(nsd, diff_cont) + lvs.connect(poly, poly_cont) + lvs.connect(poly_cont, metal1) + lvs.connect(diff_cont, metal1) + lvs.connect(diff_cont, ptie) + lvs.connect(diff_cont, ntie) + lvs.connect(nwell, ntie) + lvs.connect(metal1, via1) + lvs.connect(via1, metal2) + lvs.connect(poly, poly_lbl) # attaches labels + lvs.connect(metal1, metal1_lbl) # attaches labels + lvs.connect(metal2, metal2_lbl) # attaches labels + + # global + lvs.connect_global(ptie, "BULK") + lvs.connect_global(bulk, "BULK") + + lvs.extract_netlist + + lvs.netlist.combine_devices + + lvs.netlist.make_top_level_pins + lvs.netlist.purge + + # read the reference netlist + reader = RBA::NetlistSpiceReader::new + nl = RBA::Netlist::new + nl.read(File.join($ut_testsrc, "testdata", "algo", "lvs_test_1.spi"), reader) + + assert_equal(lvs.reference == nil, true) + lvs.reference = nl + assert_equal(lvs.reference == nl, true) + + # do the actual compare + comparer = RBA::NetlistComparer::new + res = lvs.compare(comparer) + assert_equal(res, true) + + assert_equal(lvs.xref != nil, true) + + end + + def test_3_ReadAndWrite + + lvs = RBA::LayoutVsSchematic::new + + input = File.join($ut_testsrc, "testdata", "algo", "lvs_test1b_au.lvsdb") + lvs.read(input) + + tmp = File::join($ut_testtmp, "tmp.lvsdb") + lvs.write(tmp) + + assert_equal(File.open(tmp, "r").read, File.open(input, "r").read) + + assert_equal(lvs.layer_names.join(","), "bulk,nwell,poly,poly_lbl,diff_cont,poly_cont,metal1,metal1_lbl,via1,metal2,metal2_lbl,ntie,psd,ptie,nsd") + assert_equal(lvs.name(lvs.layer_by_name("metal1")), "metal1") + assert_equal(lvs.name(lvs.layer_by_index(lvs.layer_of(lvs.layer_by_name("metal1")))), "metal1") + + tmp = File::join($ut_testtmp, "tmp.l2n") + lvs.write_l2n(tmp) + + l2n = RBA::LayoutToNetlist::new + l2n.read(tmp) + assert_equal(l2n.layer_names.join(","), "bulk,nwell,poly,poly_lbl,diff_cont,poly_cont,metal1,metal1_lbl,via1,metal2,metal2_lbl,ntie,psd,ptie,nsd") + + lvs2 = RBA::LayoutVsSchematic::new + lvs2.read_l2n(tmp) + assert_equal(lvs2.layer_names.join(","), "bulk,nwell,poly,poly_lbl,diff_cont,poly_cont,metal1,metal1_lbl,via1,metal2,metal2_lbl,ntie,psd,ptie,nsd") + + end + +end + +load("test_epilogue.rb") + diff --git a/testdata/ruby/dbNetlistCrossReference.rb b/testdata/ruby/dbNetlistCrossReference.rb new file mode 100644 index 000000000..7bd7c9ec9 --- /dev/null +++ b/testdata/ruby/dbNetlistCrossReference.rb @@ -0,0 +1,167 @@ + +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2019 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 + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBNetlistCrossReference_TestClass < TestBase + + def test_1_Basic + + xref = RBA::NetlistCrossReference::new + + lvs = RBA::LayoutVsSchematic::new + input = File.join($ut_testsrc, "testdata", "algo", "lvs_test1b_au.lvsdb") + lvs.read(input) + + reader = RBA::NetlistSpiceReader::new + nl = RBA::Netlist::new + nl.read(File.join($ut_testsrc, "testdata", "algo", "lvs_test_1.spi"), reader) + + assert_equal(xref.circuit_count, 0) + + # A NetlistCrossReference object can act as a receiver for a netlist comparer + comp = RBA::NetlistComparer::new + comp.compare(lvs.netlist, nl, xref) + + assert_equal(xref.circuit_count, 3) + + xref.clear + assert_equal(xref.circuit_count, 0) + + end + + def test_2_CircuitPairs + + lvs = RBA::LayoutVsSchematic::new + input = File.join($ut_testsrc, "testdata", "algo", "lvs_test2b_au.lvsdb") + lvs.read(input) + + xref = lvs.xref + assert_equal(xref.circuit_count, 4) + + info = [] + xref.each_circuit_pair do |cp| + info << [ cp.first, cp.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + cp.status.to_s + end + assert_equal(info.join(","), "(nil)/INV2PAIRX:Mismatch,INV2/INV2:Match,INV2PAIR/INV2PAIR:NoMatch,RINGO/RINGO:Skipped") + + cp_inv2 = nil + xref.each_circuit_pair do |cp| + if cp.first && cp.first.name == "INV2" + cp_inv2 = cp + end + end + + assert_equal(cp_inv2 != nil, true) + + info = [] + xref.each_pin_pair(cp_inv2) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "/:Match,BULK/:Match,IN/:Match,OUT/:Match,VDD/:Match,VSS/:Match") + + info = [] + xref.each_net_pair(cp_inv2) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "/1:Match,BULK/6:Match,IN/2:Match,OUT/3:Match,VDD/5:Match,VSS/4:Match") + + netp_bulk = nil + xref.each_net_pair(cp_inv2) do |p| + if p.first.name == "BULK" + netp_bulk = p + end + end + + info = [] + xref.each_net_terminal_pair(netp_bulk) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.terminal_def.name : "(nil)" }.join("/") + end + assert_equal(info.join(","), "B/B") + + info = [] + xref.each_net_pin_pair(netp_bulk) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.pin.name : "(nil)" }.join("/") + end + assert_equal(info.join(","), "BULK/") + + info = [] + xref.each_net_subcircuit_pin_pair(netp_bulk) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.pin.name : "(nil)" }.join("/") + end + assert_equal(info.join(","), "") + + info = [] + xref.each_device_pair(cp_inv2) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "/$1:Match,/$3:Match") + + info = [] + xref.each_subcircuit_pair(cp_inv2) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "") + + cp_inv2pair = nil + xref.each_circuit_pair do |cp| + if cp.first && cp.first.name == "INV2PAIR" + cp_inv2pair = cp + end + end + + assert_equal(cp_inv2pair != nil, true) + + info = [] + xref.each_pin_pair(cp_inv2pair) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "(nil)/:Mismatch,/(nil):Mismatch,/:Match,/:Match,/:Match,/:Match,/:Match,BULK/:Match") + + info = [] + xref.each_net_pair(cp_inv2pair) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "/(nil):Mismatch,/2:Mismatch,/3:Mismatch,/4:Match,/6:Match,/7:Mismatch,BULK/1:Mismatch") + + info = [] + xref.each_device_pair(cp_inv2pair) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "") + + info = [] + xref.each_subcircuit_pair(cp_inv2pair) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "(nil)/$2:Mismatch,/(nil):Mismatch,/(nil):Mismatch") + + + end + +end + +load("test_epilogue.rb") + +