From e34fc8967a49f6305fefbdff89e0d5d042bc4d7c Mon Sep 17 00:00:00 2001
From: Matthias Koefferlein
Date: Mon, 5 Jul 2021 22:22:13 +0200
Subject: [PATCH] Some enhancements
* Device#net_for_terminal with terminal name
* Spice writer now dumps all parameters for resistors and caps (also secondary)
* Enabled Spice writer delegate in LVS (spice_format(...))
* Device class factories for built-in device extractors
---
src/db/db/dbNetlistSpiceWriter.cc | 3 +
src/db/db/gsiDeclDbNetlist.cc | 30 +++++
src/drc/drc/built-in-macros/_drc_engine.rb | 103 +++++++++++----
.../lay/doc/manual/lvs_device_extractors.xml | 28 ++++
src/lvs/unit_tests/lvsSimpleTests.cc | 8 +-
testdata/lvs/enable_wl1.cir | 11 ++
testdata/lvs/enable_wl1.lvs | 52 ++++++++
testdata/lvs/enable_wl1.lvsdb | 118 +++++++++++++++++
testdata/lvs/enable_wl2.cir | 11 ++
testdata/lvs/enable_wl2.lvs | 33 +++++
testdata/lvs/enable_wl2.lvsdb | 118 +++++++++++++++++
testdata/lvs/enable_wl3.cir | 11 ++
testdata/lvs/enable_wl3.lvs | 33 +++++
testdata/lvs/enable_wl3.lvsdb | 121 ++++++++++++++++++
testdata/lvs/resistor.cir | 4 +
testdata/lvs/resistor.gds | Bin 0 -> 2048 bytes
testdata/lvs/resistor2.cir | 4 +
testdata/ruby/dbNetlist.rb | 2 +
18 files changed, 665 insertions(+), 25 deletions(-)
create mode 100644 testdata/lvs/enable_wl1.cir
create mode 100644 testdata/lvs/enable_wl1.lvs
create mode 100644 testdata/lvs/enable_wl1.lvsdb
create mode 100644 testdata/lvs/enable_wl2.cir
create mode 100644 testdata/lvs/enable_wl2.lvs
create mode 100644 testdata/lvs/enable_wl2.lvsdb
create mode 100644 testdata/lvs/enable_wl3.cir
create mode 100644 testdata/lvs/enable_wl3.lvs
create mode 100644 testdata/lvs/enable_wl3.lvsdb
create mode 100644 testdata/lvs/resistor.cir
create mode 100644 testdata/lvs/resistor.gds
create mode 100644 testdata/lvs/resistor2.cir
diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc
index 5bf03d678..b5893bee3 100644
--- a/src/db/db/dbNetlistSpiceWriter.cc
+++ b/src/db/db/dbNetlistSpiceWriter.cc
@@ -117,6 +117,7 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
os << " ";
os << format_name (dev.device_class ()->name ());
}
+ os << format_params (dev, db::DeviceClassCapacitor::param_id_C);
} else if (ind) {
@@ -129,6 +130,7 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
os << " ";
os << format_name (dev.device_class ()->name ());
}
+ os << format_params (dev, db::DeviceClassInductor::param_id_L);
} else if (res || res3) {
@@ -141,6 +143,7 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
os << " ";
os << format_name (dev.device_class ()->name ());
}
+ os << format_params (dev, db::DeviceClassResistor::param_id_R);
} else if (diode) {
diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc
index cfdc5a341..0941d63c3 100644
--- a/src/db/db/gsiDeclDbNetlist.cc
+++ b/src/db/db/gsiDeclDbNetlist.cc
@@ -251,6 +251,24 @@ static void add_other_abstracts (db::Device *device, const db::DeviceAbstractRef
device->other_abstracts ().push_back (ref);
}
+static const db::Net *net_for_terminal_by_name_const (const db::Device *device, const std::string &name)
+{
+ if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) {
+ return 0;
+ } else {
+ return device->net_for_terminal (device->device_class ()->terminal_id_for_name (name));
+ }
+}
+
+static const db::Net *net_for_terminal_by_name (db::Device *device, const std::string &name)
+{
+ if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) {
+ return 0;
+ } else {
+ return device->net_for_terminal (device->device_class ()->terminal_id_for_name (name));
+ }
+}
+
Class decl_dbDevice (decl_dbNetlistObject, "db", "Device",
gsi::method ("device_class", &db::Device::device_class,
"@brief Gets the device class the device belongs to.\n"
@@ -337,6 +355,18 @@ Class decl_dbDevice (decl_dbNetlistObject, "db", "Device",
"\n\n"
"This constness variant has been introduced in version 0.26.8"
) +
+ gsi::method_ext ("net_for_terminal", net_for_terminal_by_name_const, gsi::arg ("terminal_name"),
+ "@brief Gets the net connected to the specified terminal.\n"
+ "If the terminal is not connected, nil is returned for the net."
+ "\n\n"
+ "This convenience method has been introduced in version 0.27.3.\n"
+ ) +
+ gsi::method_ext ("net_for_terminal", net_for_terminal_by_name, gsi::arg ("terminal_name"),
+ "@brief Gets the net connected to the specified terminal (non-const version).\n"
+ "If the terminal is not connected, nil is returned for the net."
+ "\n\n"
+ "This convenience method has been introduced in version 0.27.3.\n"
+ ) +
gsi::method ("connect_terminal", &db::Device::connect_terminal, gsi::arg ("terminal_id"), gsi::arg ("net"),
"@brief Connects the given terminal to the specified net.\n"
) +
diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb
index 51f44c266..6359bff21 100644
--- a/src/drc/drc/built-in-macros/_drc_engine.rb
+++ b/src/drc/drc/built-in-macros/_drc_engine.rb
@@ -4,6 +4,15 @@ require 'pathname'
module DRC
+ class CustomDeviceClassFactory < RBA::DeviceClassFactory
+ def initialize(cls)
+ @cls = cls
+ end
+ def create_class
+ @cls.new
+ end
+ end
+
# The DRC engine
# %DRC%
@@ -308,22 +317,57 @@ module DRC
# @brief Defines SPICE output format (with options)
# @name write_spice
# @synopsis write_spice([ use_net_names [, with_comments ] ])
+ # @synopsis write_spice(writer_delegate [, use_net_names [, with_comments ] ])
# Use this option in \target_netlist for the format parameter to
# specify SPICE format.
# "use_net_names" and "with_comments" are boolean parameters indicating
# whether to use named nets (numbers if false) and whether to add
# information comments such as instance coordinates or pin names.
+ #
+ # "writer_delegate" allows using a \NetlistSpiceWriterDelegate object to
+ # control the actual writing.
+
+ def write_spice(*args)
- def write_spice(use_net_names = nil, with_comments = nil)
self._context("write_spice") do
- writer = RBA::NetlistSpiceWriter::new
+
+ delegate = nil
+ use_net_names = nil
+ with_comments = nil
+ args.each do |a|
+ if (a == false || a == true) && (use_net_names == nil || with_comments == nil)
+ if use_net_names == nil
+ use_net_names = a
+ else
+ with_comments = a
+ end
+ elsif a.is_a?(RBA::NetlistSpiceWriterDelegate)
+ delegate = a
+ else
+ raise("Too many arguments specified or argument is of wrong type: " + a.inspect)
+ end
+ end
+
+ writer = RBA::NetlistSpiceWriter::new(delegate)
if use_net_names != nil
writer.use_net_names = use_net_names
end
if with_comments != nil
writer.with_comments = with_comments
end
+
writer
+
+ end
+ end
+
+ def _make_factory(cls)
+ if !cls
+ return nil
+ elsif !cls.is_a?(Class)
+ raise("Expected a class object for the 'class' argument of device extractors")
+ else
+ CustomDeviceClassFactory::new(cls)
end
end
@@ -331,15 +375,16 @@ module DRC
# @brief Supplies the MOS3 transistor extractor class
# @name mos3
# @synopsis mos3(name)
+ # @synopsis mos3(name, class)
# Use this class with \extract_devices to specify extraction of a
# three-terminal MOS transistor.
#
# See RBA::DeviceExtractorMOS3Transistor for more details
# about this extractor (non-strict mode applies for 'mos3').
- def mos3(name)
+ def mos3(name, cls = nil)
self._context("mos3") do
- RBA::DeviceExtractorMOS3Transistor::new(name)
+ RBA::DeviceExtractorMOS3Transistor::new(name, false, _make_factory(cls))
end
end
@@ -347,15 +392,16 @@ module DRC
# @brief Supplies the MOS4 transistor extractor class
# @name mos4
# @synopsis mos4(name)
+ # @synopsis mos4(name, class)
# Use this class with \extract_devices to specify extraction of a
# four-terminal MOS transistor.
#
# See RBA::DeviceExtractorMOS4Transistor for more details
# about this extractor (non-strict mode applies for 'mos4').
- def mos4(name)
+ def mos4(name, cls = nil)
self._context("mos4") do
- RBA::DeviceExtractorMOS4Transistor::new(name)
+ RBA::DeviceExtractorMOS4Transistor::new(name, false, _make_factory(cls))
end
end
@@ -363,6 +409,7 @@ module DRC
# @brief Supplies the DMOS3 transistor extractor class
# @name dmos3
# @synopsis dmos3(name)
+ # @synopsis dmos3(name, class)
# Use this class with \extract_devices to specify extraction of a
# three-terminal DMOS transistor. A DMOS transistor is essentially
# the same than a MOS transistor, but source and drain are
@@ -371,9 +418,9 @@ module DRC
# See RBA::DeviceExtractorMOS3Transistor for more details
# about this extractor (strict mode applies for 'dmos3').
- def dmos3(name)
+ def dmos3(name, cls = nil)
self._context("dmos3") do
- RBA::DeviceExtractorMOS3Transistor::new(name, true)
+ RBA::DeviceExtractorMOS3Transistor::new(name, true, _make_factory(cls))
end
end
@@ -381,6 +428,7 @@ module DRC
# @brief Supplies the MOS4 transistor extractor class
# @name dmos4
# @synopsis dmos4(name)
+ # @synopsis dmos4(name, class)
# Use this class with \extract_devices to specify extraction of a
# four-terminal DMOS transistor. A DMOS transistor is essentially
# the same than a MOS transistor, but source and drain are
@@ -389,9 +437,9 @@ module DRC
# See RBA::DeviceExtractorMOS4Transistor for more details
# about this extractor (strict mode applies for 'dmos4').
- def dmos4(name)
+ def dmos4(name, cls = nil)
self._context("dmos4") do
- RBA::DeviceExtractorMOS4Transistor::new(name, true)
+ RBA::DeviceExtractorMOS4Transistor::new(name, true, _make_factory(cls))
end
end
@@ -399,15 +447,16 @@ module DRC
# @brief Supplies the BJT3 transistor extractor class
# @name bjt3
# @synopsis bjt3(name)
+ # @synopsis bjt3(name, class)
# Use this class with \extract_devices to specify extraction of a
# bipolar junction transistor
#
# See RBA::DeviceExtractorBJT3Transistor for more details
# about this extractor.
- def bjt3(name)
+ def bjt3(name, cls = nil)
self._context("bjt3") do
- RBA::DeviceExtractorBJT3Transistor::new(name)
+ RBA::DeviceExtractorBJT3Transistor::new(name, _make_factory(cls))
end
end
@@ -415,15 +464,16 @@ module DRC
# @brief Supplies the BJT4 transistor extractor class
# @name bjt4
# @synopsis bjt4(name)
+ # @synopsis bjt4(name, class)
# Use this class with \extract_devices to specify extraction of a
# bipolar junction transistor with a substrate terminal
#
# See RBA::DeviceExtractorBJT4Transistor for more details
# about this extractor.
- def bjt4(name)
+ def bjt4(name, cls = nil)
self._context("bjt4") do
- RBA::DeviceExtractorBJT4Transistor::new(name)
+ RBA::DeviceExtractorBJT4Transistor::new(name, _make_factory(cls))
end
end
@@ -431,15 +481,16 @@ module DRC
# @brief Supplies the diode extractor class
# @name diode
# @synopsis diode(name)
+ # @synopsis diode(name, class)
# Use this class with \extract_devices to specify extraction of a
# planar diode
#
# See RBA::DeviceExtractorDiode for more details
# about this extractor.
- def diode(name)
+ def diode(name, cls = nil)
self._context("diode") do
- RBA::DeviceExtractorDiode::new(name)
+ RBA::DeviceExtractorDiode::new(name, _make_factory(cls))
end
end
@@ -447,6 +498,7 @@ module DRC
# @brief Supplies the resistor extractor class
# @name resistor
# @synopsis resistor(name, sheet_rho)
+ # @synopsis resistor(name, sheet_rho, class)
# Use this class with \extract_devices to specify extraction of a resistor.
#
# The sheet_rho value is the sheet resistance in ohms/square. It is used
@@ -455,9 +507,9 @@ module DRC
# See RBA::DeviceExtractorResistor for more details
# about this extractor.
- def resistor(name, sheet_rho)
+ def resistor(name, sheet_rho, cls = nil)
self._context("resistor") do
- RBA::DeviceExtractorResistor::new(name, sheet_rho)
+ RBA::DeviceExtractorResistor::new(name, sheet_rho, _make_factory(cls))
end
end
@@ -465,6 +517,7 @@ module DRC
# @brief Supplies the resistor extractor class that includes a bulk terminal
# @name resistor_with_bulk
# @synopsis resistor_with_bulk(name, sheet_rho)
+ # @synopsis resistor_with_bulk(name, sheet_rho, class)
# Use this class with \extract_devices to specify extraction of a resistor
# with a bulk terminal.
# The sheet_rho value is the sheet resistance in ohms/square.
@@ -472,9 +525,9 @@ module DRC
# See RBA::DeviceExtractorResistorWithBulk for more details
# about this extractor.
- def resistor_with_bulk(name, sheet_rho)
+ def resistor_with_bulk(name, sheet_rho, cls = nil)
self._context("resistor_with_bulk") do
- RBA::DeviceExtractorResistorWithBulk::new(name, sheet_rho)
+ RBA::DeviceExtractorResistorWithBulk::new(name, sheet_rho, _make_factory(cls))
end
end
@@ -482,15 +535,16 @@ module DRC
# @brief Supplies the capacitor extractor class
# @name capacitor
# @synopsis capacitor(name, area_cap)
+ # @synopsis capacitor(name, area_cap, class)
# Use this class with \extract_devices to specify extraction of a capacitor.
# The area_cap argument is the capacitance in Farad per square micrometer.
#
# See RBA::DeviceExtractorCapacitor for more details
# about this extractor.
- def capacitor(name, area_cap)
+ def capacitor(name, area_cap, cls = nil)
self._context("capacitor") do
- RBA::DeviceExtractorCapacitor::new(name, area_cap)
+ RBA::DeviceExtractorCapacitor::new(name, area_cap, _make_factory(cls))
end
end
@@ -498,6 +552,7 @@ module DRC
# @brief Supplies the capacitor extractor class that includes a bulk terminal
# @name capacitor_with_bulk
# @synopsis capacitor_with_bulk(name, area_cap)
+ # @synopsis capacitor_with_bulk(name, area_cap, class)
# Use this class with \extract_devices to specify extraction of a capacitor
# with a bulk terminal.
# The area_cap argument is the capacitance in Farad per square micrometer.
@@ -505,9 +560,9 @@ module DRC
# See RBA::DeviceExtractorCapacitorWithBulk for more details
# about this extractor.
- def capacitor_with_bulk(name, area_cap)
+ def capacitor_with_bulk(name, area_cap, cls = nil)
self._context("capacitor_with_bulk") do
- RBA::DeviceExtractorCapacitorWithBulk::new(name, area_cap)
+ RBA::DeviceExtractorCapacitorWithBulk::new(name, area_cap, _make_factory(cls))
end
end
diff --git a/src/lay/lay/doc/manual/lvs_device_extractors.xml b/src/lay/lay/doc/manual/lvs_device_extractors.xml
index e1200800f..7cf6449e2 100644
--- a/src/lay/lay/doc/manual/lvs_device_extractors.xml
+++ b/src/lay/lay/doc/manual/lvs_device_extractors.xml
@@ -376,5 +376,33 @@ dc.enable_parameter("L", true)
fully enabled parameters.
+
+ Another way of customizing the built-in device extractors is to
+ supply a custom device class. The following code creates a new
+ resistor class which changes the preconfigured device parameter definitions
+ to enable "W" and "L".
+
+
+ class MyResistor < RBA::DeviceClassResistor
+ def initialize
+ super
+ enable_parameter("W", true)
+ enable_parameter("L", true)
+ end
+end
+
+...
+
+extract_devices(resistor("RES", 1, MyResistor), ...)
+
+
+
+ The effect of this code is the same than the first one, but using a custom
+ device class opens the option to supply additional parameters for example
+ or to implement some entirely new device while using the extraction
+ mechanics of the resistor extractor. The only requirement is compatibility of
+ the parameter and terminal definitions.
+
+
diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc
index 6d054e461..304aa9dd5 100644
--- a/src/lvs/unit_tests/lvsSimpleTests.cc
+++ b/src/lvs/unit_tests/lvsSimpleTests.cc
@@ -206,8 +206,14 @@ TEST(23_issue709)
run_test (_this, "empty_subcells", "empty_subcells.gds");
}
-// empty gds
TEST(24_issue806)
{
run_test (_this, "custom_compare", "custom_compare.gds");
}
+
+TEST(25_enableWandL)
+{
+ run_test (_this, "enable_wl1", "resistor.gds");
+ run_test (_this, "enable_wl2", "resistor.gds");
+ run_test (_this, "enable_wl3", "resistor.gds");
+}
diff --git a/testdata/lvs/enable_wl1.cir b/testdata/lvs/enable_wl1.cir
new file mode 100644
index 000000000..2dbc8c321
--- /dev/null
+++ b/testdata/lvs/enable_wl1.cir
@@ -0,0 +1,11 @@
+* Extracted by KLayout
+
+* cell Rre
+* pin gnd!
+* pin vdd!
+.SUBCKT Rre 1 2
+* net 1 gnd!
+* net 2 vdd!
+* device instance $1 r0 *1 8.43,1.51 RR1
+R$1 1 2 RR1 10 W=0.6 L=6
+.ENDS Rre
diff --git a/testdata/lvs/enable_wl1.lvs b/testdata/lvs/enable_wl1.lvs
new file mode 100644
index 000000000..a58903723
--- /dev/null
+++ b/testdata/lvs/enable_wl1.lvs
@@ -0,0 +1,52 @@
+
+# test spice writer delegate on this occasion
+class MyWriterDelegate < RBA::NetlistSpiceWriterDelegate
+ def write_device(device)
+ if device.device_class.name == "RR1"
+ line = "R"
+ line += format_name(device.expanded_name)
+ line += " "
+ line += net_to_string(device.net_for_terminal("A"))
+ line += " "
+ line += net_to_string(device.net_for_terminal("B"))
+ line += " "
+ line += format_name(device.device_class.name)
+ line += " "
+ line += "%.12g" % device.parameter("R")
+ line += " W="
+ line += "%.12g" % device.parameter("W")
+ line += " L="
+ line += "%.12g" % device.parameter("L")
+ emit_line(line)
+ else
+ super
+ end
+ end
+end
+
+source($lvs_test_source)
+report_lvs($lvs_test_target_lvsdb, true)
+target_netlist($lvs_test_target_cir, write_spice(MyWriterDelegate::new), "Extracted by KLayout")
+
+schematic("resistor.cir")
+
+deep
+
+contact = input(15, 0)
+metal1 = input(16, 0)
+metal1_ver = input(16, 5)
+metal1_lbl = labels(16, 3)
+res = metal1 & metal1_ver
+metal1_not_res = metal1 - metal1_ver
+
+dc = extract_devices(resistor("RR1", 1), { "R" => res , "C" => metal1_not_res})
+dc.enable_parameter("W", true)
+dc.enable_parameter("L", true)
+
+connect(contact, metal1_not_res)
+connect(metal1_not_res, metal1_lbl)
+
+align
+netlist.simplify
+compare
+
diff --git a/testdata/lvs/enable_wl1.lvsdb b/testdata/lvs/enable_wl1.lvsdb
new file mode 100644
index 000000000..67695b612
--- /dev/null
+++ b/testdata/lvs/enable_wl1.lvsdb
@@ -0,0 +1,118 @@
+#%lvsdb-klayout
+
+# Layout
+layout(
+ top(Rre)
+ unit(0.001)
+
+ # Layer section
+ # This section lists the mask layers (drawing or derived) and their connections.
+
+ # Mask layers
+ layer(l3 '15/0')
+ layer(l4 '16/3')
+ layer(l1)
+
+ # Mask layer connectivity
+ connect(l3 l3 l1)
+ connect(l4 l1)
+ connect(l1 l3 l4 l1)
+
+ # Device class section
+ class(RR1 RES
+ param(L 1 0)
+ param(W 1 0)
+ )
+
+ # Device abstracts section
+ # Device abstracts list the pin shapes of the devices.
+ device(D$RR1 RR1
+ terminal(A
+ rect(l1 (-3160 -300) (160 600))
+ )
+ terminal(B
+ rect(l1 (3000 -300) (160 600))
+ )
+ )
+
+ # Circuit section
+ # Circuits are the hierarchical building blocks of the netlist.
+ circuit(Rre
+
+ # Circuit boundary
+ rect((5270 1210) (6320 600))
+
+ # Nets with their geometries
+ net(1 name('gnd!')
+ rect(l3 (5295 1230) (120 560))
+ text(l4 'gnd!' (-60 -60))
+ rect(l1 (-70 -505) (125 570))
+ rect(l1 (-140 -585) (160 600))
+ )
+ net(2 name('vdd!')
+ rect(l3 (11455 1240) (120 540))
+ text(l4 'vdd!' (-65 -60))
+ rect(l1 (-65 -495) (125 560))
+ rect(l1 (-140 -575) (160 600))
+ )
+
+ # Outgoing pins and their connections to nets
+ pin(1 name('gnd!'))
+ pin(2 name('vdd!'))
+
+ # Devices and their connections
+ device(1 D$RR1
+ location(8430 1510)
+ param(R 10)
+ param(L 6)
+ param(W 0.6)
+ param(A 3.6)
+ param(P 13.2)
+ terminal(A 1)
+ terminal(B 2)
+ )
+
+ )
+)
+
+# Reference netlist
+reference(
+
+ # Device class section
+ class(RR1 RES)
+
+ # Circuit section
+ # Circuits are the hierarchical building blocks of the netlist.
+ circuit(RRE
+
+ # Nets
+ net(1 name('VDD!'))
+ net(2 name('GND!'))
+
+ # Devices and their connections
+ device(1 RR1
+ name(R0)
+ param(R 10)
+ param(L 6)
+ param(W 0.6)
+ param(A 0)
+ param(P 0)
+ terminal(A 1)
+ terminal(B 2)
+ )
+
+ )
+)
+
+# Cross reference
+xref(
+ circuit(Rre RRE match
+ xref(
+ net(1 2 match)
+ net(2 1 match)
+ pin(0 () match)
+ pin(1 () match)
+ device(1 1 match)
+ )
+ )
+)
diff --git a/testdata/lvs/enable_wl2.cir b/testdata/lvs/enable_wl2.cir
new file mode 100644
index 000000000..5ce133250
--- /dev/null
+++ b/testdata/lvs/enable_wl2.cir
@@ -0,0 +1,11 @@
+* Extracted by KLayout
+
+* cell Rre
+* pin gnd!
+* pin vdd!
+.SUBCKT Rre 1 2
+* net 1 gnd!
+* net 2 vdd!
+* device instance $1 r0 *1 8.43,1.51 RR1
+R$1 1 2 10 RR1 L=6U W=0.6U A=3.6P P=13.2U
+.ENDS Rre
diff --git a/testdata/lvs/enable_wl2.lvs b/testdata/lvs/enable_wl2.lvs
new file mode 100644
index 000000000..945a89294
--- /dev/null
+++ b/testdata/lvs/enable_wl2.lvs
@@ -0,0 +1,33 @@
+
+source($lvs_test_source)
+report_lvs($lvs_test_target_lvsdb, true)
+target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout")
+
+schematic("resistor.cir")
+
+deep
+
+contact = input(15, 0)
+metal1 = input(16, 0)
+metal1_ver = input(16, 5)
+metal1_lbl = labels(16, 3)
+res = metal1 & metal1_ver
+metal1_not_res = metal1 - metal1_ver
+
+class MyResistorClass < RBA::DeviceClassResistor
+ def initialize
+ super
+ enable_parameter("W", true)
+ enable_parameter("L", true)
+ end
+end
+
+extract_devices(resistor("RR1", 1, MyResistorClass), { "R" => res , "C" => metal1_not_res})
+
+connect(contact, metal1_not_res)
+connect(metal1_not_res, metal1_lbl)
+
+align
+netlist.simplify
+compare
+
diff --git a/testdata/lvs/enable_wl2.lvsdb b/testdata/lvs/enable_wl2.lvsdb
new file mode 100644
index 000000000..67695b612
--- /dev/null
+++ b/testdata/lvs/enable_wl2.lvsdb
@@ -0,0 +1,118 @@
+#%lvsdb-klayout
+
+# Layout
+layout(
+ top(Rre)
+ unit(0.001)
+
+ # Layer section
+ # This section lists the mask layers (drawing or derived) and their connections.
+
+ # Mask layers
+ layer(l3 '15/0')
+ layer(l4 '16/3')
+ layer(l1)
+
+ # Mask layer connectivity
+ connect(l3 l3 l1)
+ connect(l4 l1)
+ connect(l1 l3 l4 l1)
+
+ # Device class section
+ class(RR1 RES
+ param(L 1 0)
+ param(W 1 0)
+ )
+
+ # Device abstracts section
+ # Device abstracts list the pin shapes of the devices.
+ device(D$RR1 RR1
+ terminal(A
+ rect(l1 (-3160 -300) (160 600))
+ )
+ terminal(B
+ rect(l1 (3000 -300) (160 600))
+ )
+ )
+
+ # Circuit section
+ # Circuits are the hierarchical building blocks of the netlist.
+ circuit(Rre
+
+ # Circuit boundary
+ rect((5270 1210) (6320 600))
+
+ # Nets with their geometries
+ net(1 name('gnd!')
+ rect(l3 (5295 1230) (120 560))
+ text(l4 'gnd!' (-60 -60))
+ rect(l1 (-70 -505) (125 570))
+ rect(l1 (-140 -585) (160 600))
+ )
+ net(2 name('vdd!')
+ rect(l3 (11455 1240) (120 540))
+ text(l4 'vdd!' (-65 -60))
+ rect(l1 (-65 -495) (125 560))
+ rect(l1 (-140 -575) (160 600))
+ )
+
+ # Outgoing pins and their connections to nets
+ pin(1 name('gnd!'))
+ pin(2 name('vdd!'))
+
+ # Devices and their connections
+ device(1 D$RR1
+ location(8430 1510)
+ param(R 10)
+ param(L 6)
+ param(W 0.6)
+ param(A 3.6)
+ param(P 13.2)
+ terminal(A 1)
+ terminal(B 2)
+ )
+
+ )
+)
+
+# Reference netlist
+reference(
+
+ # Device class section
+ class(RR1 RES)
+
+ # Circuit section
+ # Circuits are the hierarchical building blocks of the netlist.
+ circuit(RRE
+
+ # Nets
+ net(1 name('VDD!'))
+ net(2 name('GND!'))
+
+ # Devices and their connections
+ device(1 RR1
+ name(R0)
+ param(R 10)
+ param(L 6)
+ param(W 0.6)
+ param(A 0)
+ param(P 0)
+ terminal(A 1)
+ terminal(B 2)
+ )
+
+ )
+)
+
+# Cross reference
+xref(
+ circuit(Rre RRE match
+ xref(
+ net(1 2 match)
+ net(2 1 match)
+ pin(0 () match)
+ pin(1 () match)
+ device(1 1 match)
+ )
+ )
+)
diff --git a/testdata/lvs/enable_wl3.cir b/testdata/lvs/enable_wl3.cir
new file mode 100644
index 000000000..5ce133250
--- /dev/null
+++ b/testdata/lvs/enable_wl3.cir
@@ -0,0 +1,11 @@
+* Extracted by KLayout
+
+* cell Rre
+* pin gnd!
+* pin vdd!
+.SUBCKT Rre 1 2
+* net 1 gnd!
+* net 2 vdd!
+* device instance $1 r0 *1 8.43,1.51 RR1
+R$1 1 2 10 RR1 L=6U W=0.6U A=3.6P P=13.2U
+.ENDS Rre
diff --git a/testdata/lvs/enable_wl3.lvs b/testdata/lvs/enable_wl3.lvs
new file mode 100644
index 000000000..8f5405307
--- /dev/null
+++ b/testdata/lvs/enable_wl3.lvs
@@ -0,0 +1,33 @@
+
+source($lvs_test_source)
+report_lvs($lvs_test_target_lvsdb, true)
+target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout")
+
+schematic("resistor2.cir")
+
+deep
+
+contact = input(15, 0)
+metal1 = input(16, 0)
+metal1_ver = input(16, 5)
+metal1_lbl = labels(16, 3)
+res = metal1 & metal1_ver
+metal1_not_res = metal1 - metal1_ver
+
+class MyResistorClass < RBA::DeviceClassResistor
+ def initialize
+ super
+ enable_parameter("W", true)
+ enable_parameter("L", true)
+ end
+end
+
+extract_devices(resistor("RR1", 1, MyResistorClass), { "R" => res , "C" => metal1_not_res})
+
+connect(contact, metal1_not_res)
+connect(metal1_not_res, metal1_lbl)
+
+align
+netlist.simplify
+compare
+
diff --git a/testdata/lvs/enable_wl3.lvsdb b/testdata/lvs/enable_wl3.lvsdb
new file mode 100644
index 000000000..947fc54ac
--- /dev/null
+++ b/testdata/lvs/enable_wl3.lvsdb
@@ -0,0 +1,121 @@
+#%lvsdb-klayout
+
+# Layout
+layout(
+ top(Rre)
+ unit(0.001)
+
+ # Layer section
+ # This section lists the mask layers (drawing or derived) and their connections.
+
+ # Mask layers
+ layer(l3 '15/0')
+ layer(l4 '16/3')
+ layer(l1)
+
+ # Mask layer connectivity
+ connect(l3 l3 l1)
+ connect(l4 l1)
+ connect(l1 l3 l4 l1)
+
+ # Device class section
+ class(RR1 RES
+ param(L 1 0)
+ param(W 1 0)
+ )
+
+ # Device abstracts section
+ # Device abstracts list the pin shapes of the devices.
+ device(D$RR1 RR1
+ terminal(A
+ rect(l1 (-3160 -300) (160 600))
+ )
+ terminal(B
+ rect(l1 (3000 -300) (160 600))
+ )
+ )
+
+ # Circuit section
+ # Circuits are the hierarchical building blocks of the netlist.
+ circuit(Rre
+
+ # Circuit boundary
+ rect((5270 1210) (6320 600))
+
+ # Nets with their geometries
+ net(1 name('gnd!')
+ rect(l3 (5295 1230) (120 560))
+ text(l4 'gnd!' (-60 -60))
+ rect(l1 (-70 -505) (125 570))
+ rect(l1 (-140 -585) (160 600))
+ )
+ net(2 name('vdd!')
+ rect(l3 (11455 1240) (120 540))
+ text(l4 'vdd!' (-65 -60))
+ rect(l1 (-65 -495) (125 560))
+ rect(l1 (-140 -575) (160 600))
+ )
+
+ # Outgoing pins and their connections to nets
+ pin(1 name('gnd!'))
+ pin(2 name('vdd!'))
+
+ # Devices and their connections
+ device(1 D$RR1
+ location(8430 1510)
+ param(R 10)
+ param(L 6)
+ param(W 0.6)
+ param(A 3.6)
+ param(P 13.2)
+ terminal(A 1)
+ terminal(B 2)
+ )
+
+ )
+)
+
+# Reference netlist
+reference(
+
+ # Device class section
+ class(RR1 RES)
+
+ # Circuit section
+ # Circuits are the hierarchical building blocks of the netlist.
+ circuit(RRE
+
+ # Nets
+ net(1 name('VDD!'))
+ net(2 name('GND!'))
+
+ # Devices and their connections
+ device(1 RR1
+ name(R0)
+ param(R 10)
+ param(L 7)
+ param(W 0.6)
+ param(A 0)
+ param(P 0)
+ terminal(A 1)
+ terminal(B 2)
+ )
+
+ )
+)
+
+# Cross reference
+xref(
+ circuit(Rre RRE nomatch
+ xref(
+ net(() 2 mismatch)
+ net(() 1 mismatch)
+ net(1 () mismatch)
+ net(2 () mismatch)
+ pin(0 () match)
+ pin(1 () match)
+ device(() 1 mismatch)
+ device(1 () mismatch)
+ )
+ )
+)
diff --git a/testdata/lvs/resistor.cir b/testdata/lvs/resistor.cir
new file mode 100644
index 000000000..c210ac37c
--- /dev/null
+++ b/testdata/lvs/resistor.cir
@@ -0,0 +1,4 @@
+.SUBCKT Rre
+RR0 vdd! gnd! 10 RR1 W=600n L=6u M=1
+.ENDS
+
diff --git a/testdata/lvs/resistor.gds b/testdata/lvs/resistor.gds
new file mode 100644
index 0000000000000000000000000000000000000000..aa2b5869b6abf853da7d377b5e9a885063a1a273
GIT binary patch
literal 2048
zcmZQzV_;%nWsqTHVyFbtEI^(P13MDijDd%V&C$g*z9KC#i9v*!)y}cg-22$_50)-F
z^cT#I3Sp381?gpBkYiwD;AAjE(#y`k!NwL;l*+)s!o$G8#>>R;jDbyzi2=wKX9U{D
zCdCM3^GLHgH~?u7;1OVEU{H9+z`**9K}3=*+95NqxFj(zIn^&QH#HV$jvx?oK+FdPf{4Rtpgg92pcasRovlE9Cm9%YjSw{3Plz~-2Fl~q
zFR~P(PgDs(vjfcqiNk1o`YCn4$TXn6K>x$c0ns3TgT!GpUjIYGP1g-#4u}Tn1BEAy
zhUtgKB{2MfamkDxm-c7C-~uErb-n>3YX<{|BwJZZiXtTZNYO9y8mRveNPl`BOg}XK
z`N8oIjX#m~KzE-5`d=MEv;715A1Dr^f%3@lhoWC+KTzKdVEmgSXtpmv!-3*38YmCe
Y&kj_=0!go<03D!XNB{r;
literal 0
HcmV?d00001
diff --git a/testdata/lvs/resistor2.cir b/testdata/lvs/resistor2.cir
new file mode 100644
index 000000000..38882671e
--- /dev/null
+++ b/testdata/lvs/resistor2.cir
@@ -0,0 +1,4 @@
+.SUBCKT Rre
+RR0 vdd! gnd! 10 RR1 W=600n L=7u M=1
+.ENDS
+
diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb
index bb1d34570..dc09d7f4b 100644
--- a/testdata/ruby/dbNetlist.rb
+++ b/testdata/ruby/dbNetlist.rb
@@ -331,6 +331,8 @@ class DBNetlist_TestClass < TestBase
assert_equal(net.terminal_count, 1)
assert_equal(d1.net_for_terminal(1).name, "NET")
+ assert_equal(d1.net_for_terminal("B").name, "NET")
+ assert_equal(d1.net_for_terminal("X").inspect, "nil")
assert_equal(d1.net_for_terminal(0).inspect, "nil")
d1.disconnect_terminal("B")