mirror of https://github.com/KLayout/klayout.git
WIP: GSI binding of LVS framework, tests and debugging
This commit is contained in:
parent
14bc72039e
commit
89cbe930ae
|
|
@ -260,7 +260,7 @@ Class<db::LayoutToNetlist> 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"
|
||||
|
|
|
|||
|
|
@ -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<db::LayoutVsSchematic> decl_dbLayoutVsSchematic ("db", "LayoutVsSchematic",
|
||||
static void load_l2n (db::LayoutVsSchematic *lvs, const std::string &path)
|
||||
{
|
||||
lvs->db::LayoutToNetlist::load (path);
|
||||
}
|
||||
|
||||
Class<db::LayoutVsSchematic> 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<db::LayoutVsSchematic> 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"
|
||||
|
|
|
|||
|
|
@ -33,6 +33,66 @@
|
|||
namespace gsi
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// @@@@
|
||||
|
||||
template <class X, class I, class Transfer>
|
||||
class ConstMethodBiIterWithTransfer
|
||||
: public MethodSpecificBase <X>
|
||||
{
|
||||
public:
|
||||
typedef IterAdaptorAbstractBase iter_adaptor_base_type;
|
||||
typedef IterAdaptor<I> iter_adaptor_type;
|
||||
|
||||
ConstMethodBiIterWithTransfer (const std::string &name, I (X::*b) () const, I (X::*e) () const, const std::string &doc)
|
||||
: MethodSpecificBase <X> (name, doc, true, false, 0), m_b (b), m_e (e)
|
||||
{
|
||||
}
|
||||
|
||||
ConstMethodBiIterWithTransfer *add_args ()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
void initialize ()
|
||||
{
|
||||
this->clear ();
|
||||
this->template set_return<iter_adaptor_type, Transfer> ();
|
||||
}
|
||||
|
||||
virtual MethodBase *clone () const
|
||||
{
|
||||
return new ConstMethodBiIterWithTransfer (*this);
|
||||
}
|
||||
|
||||
virtual void call (void *cls, SerialArgs &, SerialArgs &ret) const
|
||||
{
|
||||
this->mark_called ();
|
||||
ret.write<iter_adaptor_base_type *> (static_cast<iter_adaptor_base_type *> (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 <class X, class I, class Transfer>
|
||||
ConstMethodBiIterWithTransfer <X, I, Transfer> *
|
||||
_iterator_with_transfer (const std::string &name, I (X::*b) () const, I (X::*e) () const, const std::string &doc)
|
||||
{
|
||||
return new ConstMethodBiIterWithTransfer <X, I, Transfer> (name, b, e, doc);
|
||||
}
|
||||
|
||||
template <class X, class I, class Transfer>
|
||||
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<X, I, Transfer> (name, b, e, doc));
|
||||
}
|
||||
|
||||
// @@@@
|
||||
// ------------------------------------------------------------
|
||||
|
||||
Class<db::Pin> decl_dbPin ("db", "Pin",
|
||||
gsi::method ("id", &db::Pin::id,
|
||||
"@brief Gets the ID of the pin.\n"
|
||||
|
|
@ -265,7 +325,7 @@ Class<db::Device> 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<db::Device> 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<db::Device> decl_dbDevice ("db", "Device",
|
|||
);
|
||||
|
||||
Class<db::DeviceAbstract> 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<db::SubCircuit> 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<db::SubCircuit> 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<db::NetTerminalRef> 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<db::NetPinRef> 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<db::NetSubcircuitPinRef> 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<db::Net> 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<db::Net, db::Net::const_pin_iterator, gsi::arg_make_reference> ("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<db::Net, db::Net::const_subcircuit_pin_iterator, gsi::arg_make_reference> ("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<db::Net, db::Net::const_terminal_iterator, gsi::arg_make_reference> ("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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
@ -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")
|
||||
|
||||
|
||||
Loading…
Reference in New Issue