diff --git a/src/drc/drc/built-in-macros/drc_install.lym b/src/drc/drc/built-in-macros/drc_install.lym
new file mode 100644
index 000000000..d31b12087
--- /dev/null
+++ b/src/drc/drc/built-in-macros/drc_install.lym
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+ true
+ false
+
+ false
+
+
+ ruby
+
+
+# Installs the home menu entries (needs to be done on autorun, not autorun-early)
+
+if RBA::Application::instance && RBA::Application::instance.main_window
+
+ cat = "drc"
+
+ name = cat.upcase
+
+ mw = RBA::Application::instance.main_window
+ mw.menu.insert_menu("tools_menu.verification_group+", cat, name)
+
+ new_action = RBA::Action::new
+ new_action.title = "New #{name} Script"
+ new_action.on_triggered do
+ mw.show_macro_editor(cat, true)
+ end
+
+ mw.menu.insert_item("tools_menu.#{cat}.end", "new_script", new_action)
+
+ edit_action = RBA::Action::new
+ edit_action.title = "Edit #{name} Script"
+ edit_action.on_triggered do
+ mw.show_macro_editor(cat, false)
+ end
+
+ mw.menu.insert_item("tools_menu.#{cat}.end", "edit_script", edit_action)
+
+end
+
+
+
diff --git a/src/drc/drc/built-in-macros/install.lym b/src/drc/drc/built-in-macros/install.lym
deleted file mode 100644
index a4fcb9fcf..000000000
--- a/src/drc/drc/built-in-macros/install.lym
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
- true
- false
-
- false
-
-
- ruby
-
-
-# Installs the home menu entries (needs to be done on autorun, not autorun-early)
-
-if RBA::Application::instance && RBA::Application::instance.main_window
-
- [ "drc", "lvs" ].each do |cat|
-
- name = cat.upcase
-
- mw = RBA::Application::instance.main_window
- mw.menu.insert_menu("tools_menu.verification_group+", cat, name)
-
- new_action = RBA::Action::new
- new_action.title = "New #{name} Script"
- new_action.on_triggered do
- mw.show_macro_editor(cat, true)
- end
-
- mw.menu.insert_item("tools_menu.#{cat}.end", "new_script", new_action)
-
- edit_action = RBA::Action::new
- edit_action.title = "Edit #{name} Script"
- edit_action.on_triggered do
- mw.show_macro_editor(cat, false)
- end
-
- mw.menu.insert_item("tools_menu.#{cat}.end", "edit_script", edit_action)
-
- end
-
-end
-
-
-
diff --git a/src/drc/drc/drcResources.qrc b/src/drc/drc/drcResources.qrc
index 5eaca5614..7b6442477 100644
--- a/src/drc/drc/drcResources.qrc
+++ b/src/drc/drc/drcResources.qrc
@@ -7,9 +7,6 @@
built-in-macros/_drc_source.rb
built-in-macros/_drc_tags.rb
built-in-macros/drc_interpreters.lym
- built-in-macros/_lvs_netter.rb
- built-in-macros/_lvs_engine.rb
- built-in-macros/lvs_interpreters.lym
- built-in-macros/install.lym
+ built-in-macros/drc_install.lym
diff --git a/src/klayout.pro b/src/klayout.pro
index e68e92eb6..cee1039c9 100644
--- a/src/klayout.pro
+++ b/src/klayout.pro
@@ -69,9 +69,10 @@ plugins.depends += lib rdb db
}
equals(HAVE_RUBY, "1") {
- SUBDIRS += drc
- MAIN_DEPENDS += drc
+ SUBDIRS += drc lvs
+ MAIN_DEPENDS += drc lvs
drc.depends += rdb lym
+ lvs.depends += drc
}
equals(HAVE_QTBINDINGS, "1") {
diff --git a/src/lvs/lvs.pro b/src/lvs/lvs.pro
new file mode 100644
index 000000000..e85b60c14
--- /dev/null
+++ b/src/lvs/lvs.pro
@@ -0,0 +1,6 @@
+
+TEMPLATE = subdirs
+SUBDIRS = lvs unit_tests
+
+unit_tests.depends += lvs
+
diff --git a/src/drc/drc/built-in-macros/_lvs_engine.rb b/src/lvs/lvs/built-in-macros/_lvs_engine.rb
similarity index 100%
rename from src/drc/drc/built-in-macros/_lvs_engine.rb
rename to src/lvs/lvs/built-in-macros/_lvs_engine.rb
diff --git a/src/drc/drc/built-in-macros/_lvs_netter.rb b/src/lvs/lvs/built-in-macros/_lvs_netter.rb
similarity index 100%
rename from src/drc/drc/built-in-macros/_lvs_netter.rb
rename to src/lvs/lvs/built-in-macros/_lvs_netter.rb
diff --git a/src/lvs/lvs/built-in-macros/lvs_install.lym b/src/lvs/lvs/built-in-macros/lvs_install.lym
new file mode 100644
index 000000000..c3fc68d57
--- /dev/null
+++ b/src/lvs/lvs/built-in-macros/lvs_install.lym
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+ true
+ false
+
+ false
+
+
+ ruby
+
+
+# Installs the home menu entries (needs to be done on autorun, not autorun-early)
+
+if RBA::Application::instance && RBA::Application::instance.main_window
+
+ cat = "lvs"
+
+ name = cat.upcase
+
+ mw = RBA::Application::instance.main_window
+ mw.menu.insert_menu("tools_menu.verification_group+", cat, name)
+
+ new_action = RBA::Action::new
+ new_action.title = "New #{name} Script"
+ new_action.on_triggered do
+ mw.show_macro_editor(cat, true)
+ end
+
+ mw.menu.insert_item("tools_menu.#{cat}.end", "new_script", new_action)
+
+ edit_action = RBA::Action::new
+ edit_action.title = "Edit #{name} Script"
+ edit_action.on_triggered do
+ mw.show_macro_editor(cat, false)
+ end
+
+ mw.menu.insert_item("tools_menu.#{cat}.end", "edit_script", edit_action)
+
+end
+
+
+
diff --git a/src/drc/drc/built-in-macros/lvs_interpreters.lym b/src/lvs/lvs/built-in-macros/lvs_interpreters.lym
similarity index 100%
rename from src/drc/drc/built-in-macros/lvs_interpreters.lym
rename to src/lvs/lvs/built-in-macros/lvs_interpreters.lym
diff --git a/src/lvs/lvs/lvs.pro b/src/lvs/lvs/lvs.pro
new file mode 100644
index 000000000..81437c6da
--- /dev/null
+++ b/src/lvs/lvs/lvs.pro
@@ -0,0 +1,22 @@
+
+DESTDIR = $$OUT_PWD/../..
+TARGET = klayout_lvs
+
+include($$PWD/../../lib.pri)
+
+DEFINES += MAKE_LVS_LIBRARY
+
+SOURCES = \
+ lvsForceLink.cc \
+
+HEADERS = \
+ lvsCommon.h \
+ lvsForceLink.h \
+
+RESOURCES = \
+ lvsResources.qrc
+
+INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC $$LYM_INC $$RDB_INC
+DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC $$LYM_INC $$RDB_INC
+LIBS += -L$$DESTDIR -lklayout_tl -lklayout_db -lklayout_gsi -lklayout_lym -lklayout_rdb -lklayout_drc
+
diff --git a/src/lvs/lvs/lvsCommon.h b/src/lvs/lvs/lvsCommon.h
new file mode 100644
index 000000000..a4a8aa968
--- /dev/null
+++ b/src/lvs/lvs/lvsCommon.h
@@ -0,0 +1,51 @@
+
+/*
+
+ 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 !defined(HDR_lvsCommon_h)
+# define HDR_lvsCommon_h
+
+# if defined _WIN32 || defined __CYGWIN__
+
+# ifdef MAKE_LVS_LIBRARY
+# define LVS_PUBLIC __declspec(dllexport)
+# else
+# define LVS_PUBLIC __declspec(dllimport)
+# endif
+# define LVS_LOCAL
+# define LVS_PUBLIC_TEMPLATE
+
+# else
+
+# if __GNUC__ >= 4 || defined(__clang__)
+# define LVS_PUBLIC __attribute__ ((visibility ("default")))
+# define LVS_PUBLIC_TEMPLATE __attribute__ ((visibility ("default")))
+# define LVS_LOCAL __attribute__ ((visibility ("hidden")))
+# else
+# define LVS_PUBLIC
+# define LVS_PUBLIC_TEMPLATE
+# define LVS_LOCAL
+# endif
+
+# endif
+
+#endif
diff --git a/src/lvs/lvs/lvsForceLink.cc b/src/lvs/lvs/lvsForceLink.cc
new file mode 100644
index 000000000..19b45e863
--- /dev/null
+++ b/src/lvs/lvs/lvsForceLink.cc
@@ -0,0 +1,34 @@
+
+/*
+
+ 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
+
+*/
+
+
+#include "lvsForceLink.h"
+#include "rdbForceLink.h"
+
+namespace lvs
+{
+ int _force_link_f ()
+ {
+ return 0;
+ }
+}
+
diff --git a/src/lvs/lvs/lvsForceLink.h b/src/lvs/lvs/lvsForceLink.h
new file mode 100644
index 000000000..7812cc8a8
--- /dev/null
+++ b/src/lvs/lvs/lvsForceLink.h
@@ -0,0 +1,40 @@
+
+/*
+
+ 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
+
+*/
+
+
+#ifndef HDR_lvsForceLink
+#define HDR_lvsForceLink
+
+#include "lvsCommon.h"
+
+/**
+ * @file Include this function to force linking of the lvs module
+ */
+
+namespace lvs
+{
+ LVS_PUBLIC int _force_link_f ();
+ int _force_link_target = _force_link_f ();
+}
+
+#endif
+
diff --git a/src/lvs/lvs/lvsResources.qrc b/src/lvs/lvs/lvsResources.qrc
new file mode 100644
index 000000000..13f1060f5
--- /dev/null
+++ b/src/lvs/lvs/lvsResources.qrc
@@ -0,0 +1,8 @@
+
+
+ built-in-macros/_lvs_netter.rb
+ built-in-macros/_lvs_engine.rb
+ built-in-macros/lvs_interpreters.lym
+ built-in-macros/lvs_install.lym
+
+
diff --git a/src/lvs/unit_tests/lvsBasicTests.cc b/src/lvs/unit_tests/lvsBasicTests.cc
new file mode 100644
index 000000000..7f710ecf8
--- /dev/null
+++ b/src/lvs/unit_tests/lvsBasicTests.cc
@@ -0,0 +1,162 @@
+
+/*
+
+ 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
+
+*/
+
+#include "tlUnitTest.h"
+#include "dbReader.h"
+#include "dbTestSupport.h"
+#include "lymMacro.h"
+
+TEST(1)
+{
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/inv.oas";
+ std::string schematic = "inv.cir"; // relative to inv.oas
+ std::string au_cir = tl::testsrc ();
+ au_cir += "/testdata/lvs/inv_layout.cir";
+ std::string au_lvsdb = tl::testsrc ();
+ au_lvsdb += "/testdata/lvs/inv.lvsdb";
+
+ std::string output_cir = this->tmp_file ("tmp.cir");
+ std::string output_lvsdb = this->tmp_file ("tmp.lvsdb");
+
+ lym::Macro lvs;
+ lvs.set_text (tl::sprintf (
+ "source('%s', 'INVERTER')\n"
+ "\n"
+ "deep\n"
+ "\n"
+ "# Reports generated\n"
+ "\n"
+ "# LVS report to inv.lvsdb\n"
+ "report_lvs('%s')\n"
+ "\n"
+ "# Write extracted netlist to inv_extracted.cir\n"
+ "target_netlist('%s', write_spice, 'Extracted by KLayout')\n"
+ "\n"
+ "# Drawing layers\n"
+ "\n"
+ "nwell = input(1, 0)\n"
+ "active = input(2, 0)\n"
+ "pplus = input(3, 0)\n"
+ "nplus = input(4, 0)\n"
+ "poly = input(5, 0)\n"
+ "contact = input(6, 0)\n"
+ "metal1 = input(7, 0)\n"
+ "metal1_lbl = labels(7, 1)\n"
+ "via1 = input(8, 0)\n"
+ "metal2 = input(9, 0)\n"
+ "metal2_lbl = labels(9, 1)\n"
+ "\n"
+ "# Bulk layer for terminal provisioning\n"
+ "\n"
+ "bulk = polygon_layer\n"
+ "\n"
+ "# Computed layers\n"
+ "\n"
+ "active_in_nwell = active & nwell\n"
+ "pactive = active_in_nwell & pplus\n"
+ "pgate = pactive & poly\n"
+ "psd = pactive - pgate\n"
+ "\n"
+ "active_outside_nwell = active - nwell\n"
+ "nactive = active_outside_nwell & nplus\n"
+ "ngate = nactive & poly\n"
+ "nsd = nactive - ngate\n"
+ "\n"
+ "# Device extraction\n"
+ "\n"
+ "# PMOS transistor device extraction\n"
+ "extract_devices(mos4('PMOS'), { 'SD' => psd, 'G' => pgate, 'W' => nwell, \n"
+ " 'tS' => psd, 'tD' => psd, 'tG' => poly, 'tW' => nwell })\n"
+ "\n"
+ "# NMOS transistor device extraction\n"
+ "extract_devices(mos4('NMOS'), { 'SD' => nsd, 'G' => ngate, 'W' => bulk, \n"
+ " 'tS' => nsd, 'tD' => nsd, 'tG' => poly, 'tW' => bulk })\n"
+ "\n"
+ "# Define connectivity for netlist extraction\n"
+ "\n"
+ "# Inter-layer\n"
+ "connect(psd, contact)\n"
+ "connect(nsd, contact)\n"
+ "connect(poly, contact)\n"
+ "connect(contact, metal1)\n"
+ "connect(metal1, metal1_lbl) # attaches labels\n"
+ "connect(metal1, via1)\n"
+ "connect(via1, metal2)\n"
+ "connect(metal2, metal2_lbl) # attaches labels\n"
+ "\n"
+ "# Global\n"
+ "connect_global(bulk, 'SUBSTRATE')\n"
+ "connect_global(nwell, 'NWELL')\n"
+ "\n"
+ "# Compare section\n"
+ "\n"
+ "schematic('%s')\n"
+ "\n"
+ "compare\n"
+ , input, output_lvsdb, output_cir, schematic)
+ );
+ lvs.set_interpreter (lym::Macro::DSLInterpreter);
+ lvs.set_dsl_interpreter ("lvs-dsl");
+
+ EXPECT_EQ (lvs.run (), 0);
+
+ compare_text_files (output_cir, au_cir);
+ compare_text_files (output_lvsdb, au_lvsdb);
+}
+
+#if 0
+TEST(2)
+{
+ lym::Macro lvs;
+ lvs.set_text (
+ "dbu 0.001\n"
+ "def compare(a, b, ex)\n"
+ " a = a.to_s\n"
+ " b = b.to_s\n"
+ " if a != b\n"
+ " raise(ex + \" (actual=#{a}, ref=#{b})\")\n"
+ " end\n"
+ "end\n"
+ "compare(0.1.um, 0.1, \"unexpected value when converting um\")\n"
+ "compare(0.1.micron, 0.1, \"unexpected value when converting micron\")\n"
+ "compare(0.1.um2, 0.1, \"unexpected value when converting um2\")\n"
+ "compare(0.1.mm2, 100000.0, \"unexpected value when converting mm2\")\n"
+ "compare(120.dbu, 0.12, \"unexpected value when converting dbu\")\n"
+ "compare((0.1.um + 120.dbu), 0.22, \"unexpected value when adding values\")\n"
+ "compare(0.1.mm, 100.0, \"unexpected value when converting mm\")\n"
+ "compare(1e-6.m, 1.0, \"unexpected value when converting m\")\n"
+ "compare(1.um, 1.0, \"unexpected value when converting integer um\")\n"
+ "compare(1.micron, 1.0, \"unexpected value when convering integer micron\")\n"
+ "compare(1.um2, 1.0, \"unexpected value when converting integer um2\")\n"
+ "compare(1.mm2, 1000000.0, \"unexpected value when converting integer mm2\")\n"
+ "compare((1.um + 120.dbu), 1.12, \"unexpected value when adding integer values\")\n"
+ "compare(1.mm, 1000.0, \"unexpected value when converting integer mm\")\n"
+ "compare(1.m, 1000000.0, \"unexpected value when converting integer m\")\n"
+ );
+ lvs.set_interpreter (lym::Macro::DSLInterpreter);
+ lvs.set_dsl_interpreter ("lvs-dsl");
+
+ EXPECT_EQ (lvs.run (), 0);
+}
+#endif
+
diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc
new file mode 100644
index 000000000..a44e3bbf5
--- /dev/null
+++ b/src/lvs/unit_tests/lvsSimpleTests.cc
@@ -0,0 +1,623 @@
+
+/*
+
+ 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
+
+*/
+
+#include "tlUnitTest.h"
+#include "dbReader.h"
+#include "dbTestSupport.h"
+#include "dbNetlist.h"
+#include "dbNetlistSpiceReader.h"
+#include "lymMacro.h"
+#include "tlFileUtils.h"
+
+#if 0 // @@@
+TEST(1)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_1.lvs";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au1.gds";
+
+ std::string output = this->tmp_file ("tmp.gds");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = nil\n"
+ "$lvs_test_target = '%s'\n"
+ , output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::Reader reader (stream);
+ reader.read (layout);
+ }
+
+ db::compare_layouts (_this, layout, au, db::NoNormalization);
+}
+
+TEST(2)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_2.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/lvstest.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au2.gds";
+
+ std::string output = this->tmp_file ("tmp.gds");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ , input, output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::Reader reader (stream);
+ reader.read (layout);
+ }
+
+ db::compare_layouts (_this, layout, au, db::NoNormalization);
+}
+
+TEST(3_Flat)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_3.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/lvstest.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au3.gds";
+
+ std::string output = this->tmp_file ("tmp.gds");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ , input, output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::Reader reader (stream);
+ reader.read (layout);
+ }
+
+ db::compare_layouts (_this, layout, au, db::NoNormalization);
+}
+
+TEST(4_Hierarchical)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_4.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/lvstest.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au4.gds";
+
+ std::string output = this->tmp_file ("tmp.gds");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ , input, output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::Reader reader (stream);
+ reader.read (layout);
+ }
+
+ db::compare_layouts (_this, layout, au, db::NoNormalization);
+}
+
+TEST(5_FlatAntenna)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_5.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/antenna_l1.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au5.gds";
+
+ std::string output = this->tmp_file ("tmp.gds");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ , input, output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::Reader reader (stream);
+ reader.read (layout);
+ }
+
+ db::compare_layouts (_this, layout, au, db::NoNormalization);
+}
+
+TEST(6_HierarchicalAntenna)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_6.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/antenna_l1.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au6.gds";
+
+ std::string output = this->tmp_file ("tmp.gds");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ , input, output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::Reader reader (stream);
+ reader.read (layout);
+ }
+
+ db::compare_layouts (_this, layout, au, db::NoNormalization);
+}
+
+TEST(7_AntennaWithDiodes)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_7.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/antenna_l1.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au7.gds";
+
+ std::string output = this->tmp_file ("tmp.gds");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ , input, output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::Reader reader (stream);
+ reader.read (layout);
+ }
+
+ db::compare_layouts (_this, layout, au, db::NoNormalization);
+}
+
+TEST(8_TextsAndPolygons)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_8.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/texts.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au8.gds";
+
+ std::string output = this->tmp_file ("tmp.gds");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ , input, output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::Reader reader (stream);
+ reader.read (layout);
+ }
+
+ db::compare_layouts (_this, layout, au, db::NoNormalization);
+}
+
+static void compare_netlists (tl::TestBase *_this, const std::string &cir, const std::string &cir_au)
+{
+ db::Netlist nl, nl_au;
+
+ db::NetlistSpiceReader reader;
+
+ {
+ tl::info << "Output: " << cir;
+ tl::InputStream is (cir);
+ reader.read (is, nl);
+ }
+
+ {
+ tl::info << "Golden: " << cir_au;
+ tl::InputStream is (cir_au);
+ reader.read (is, nl_au);
+ }
+
+ db::compare_netlist (_this, nl, nl_au);
+}
+
+TEST(9_NetlistExtraction)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_9.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/ringo.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au9a.cir";
+
+ std::string au_simplified = tl::testsrc ();
+ au_simplified += "/testdata/lvs/lvsSimpleTests_au9b.cir";
+
+ std::string output = this->tmp_file ("tmp.cir");
+ std::string output_simplified = this->tmp_file ("tmp_simplified.cir");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ "$lvs_test_target_simplified = '%s'\n"
+ , input, output, output_simplified)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+
+ // verify
+
+ CHECKPOINT ();
+ compare_netlists (_this, output, au);
+
+ CHECKPOINT ();
+ compare_netlists (_this, output_simplified, au_simplified);
+}
+
+TEST(10_NetlistExtractionFlat)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_10.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/ringo.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au10a.cir";
+
+ std::string au_simplified = tl::testsrc ();
+ au_simplified += "/testdata/lvs/lvsSimpleTests_au10b.cir";
+
+ std::string output = this->tmp_file ("tmp.cir");
+ std::string output_simplified = this->tmp_file ("tmp_simplified.cir");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ "$lvs_test_target_simplified = '%s'\n"
+ , input, output, output_simplified)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+
+ // verify
+
+ CHECKPOINT ();
+ compare_netlists (_this, output, au);
+
+ CHECKPOINT ();
+ compare_netlists (_this, output_simplified, au_simplified);
+}
+
+TEST(11_CustomDevices)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_11.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/vdiv.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au11a.cir";
+
+ std::string au_simplified = tl::testsrc ();
+ au_simplified += "/testdata/lvs/lvsSimpleTests_au11b.cir";
+
+ std::string output = this->tmp_file ("tmp.cir");
+ std::string output_simplified = this->tmp_file ("tmp_simplified.cir");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ "$lvs_test_target_simplified = '%s'\n"
+ , input, output, output_simplified)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+
+ // verify
+
+ CHECKPOINT ();
+ compare_netlists (_this, output, au);
+
+ CHECKPOINT ();
+ compare_netlists (_this, output_simplified, au_simplified);
+}
+
+TEST(12_NetlistJoinLabels)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_12.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/implicit_nets.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au12a.cir";
+
+ std::string output = this->tmp_file ("tmp.cir");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ "$lvs_test_target_simplified = nil\n"
+ , input, output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+ // verify
+
+ CHECKPOINT ();
+ compare_netlists (_this, output, au);
+}
+
+TEST(13a_KissingCorners)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_13a.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/kissing_corners.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au13a.gds";
+
+ std::string output = this->tmp_file ("tmp.gds");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ , input, output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+ // verify
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::Reader reader (stream);
+ reader.read (layout);
+ }
+
+ CHECKPOINT ();
+ db::compare_layouts (_this, layout, au, db::NoNormalization);
+}
+
+TEST(13b_KissingCornersDeep)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/lvs/lvsSimpleTests_13b.lvs";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/lvs/kissing_corners.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/lvs/lvsSimpleTests_au13b.gds";
+
+ std::string output = this->tmp_file ("tmp.gds");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$lvs_test_source = '%s'\n"
+ "$lvs_test_target = '%s'\n"
+ , input, output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro lvs;
+ lvs.load_from (rs);
+ EXPECT_EQ (lvs.run (), 0);
+
+ // verify
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::Reader reader (stream);
+ reader.read (layout);
+ }
+
+ CHECKPOINT ();
+ db::compare_layouts (_this, layout, au, db::NoNormalization);
+}
+#endif
diff --git a/src/lvs/unit_tests/unit_tests.pro b/src/lvs/unit_tests/unit_tests.pro
new file mode 100644
index 000000000..f9f5fc15f
--- /dev/null
+++ b/src/lvs/unit_tests/unit_tests.pro
@@ -0,0 +1,16 @@
+
+DESTDIR_UT = $$OUT_PWD/../..
+DESTDIR = $$OUT_PWD/..
+
+TARGET = lvs_tests
+
+include($$PWD/../../lib_ut.pri)
+
+SOURCES = \
+ lvsBasicTests.cc \
+ lvsSimpleTests.cc \
+
+INCLUDEPATH += $$DRC_INC $$TL_INC $$RDB_INC $$DB_INC $$GSI_INC $$LYM_INC
+DEPENDPATH += $$DRC_INC $$TL_INC $$RDB_INC $$DB_INC $$GSI_INC $$LYM_INC
+
+LIBS += -L$$DESTDIR_UT -lklayout_lvs -lklayout_drc -lklayout_rdb -lklayout_db -lklayout_tl -lklayout_gsi -lklayout_lym
diff --git a/testdata/lvs/inv.lvsdb b/testdata/lvs/inv.lvsdb
new file mode 100644
index 000000000..e6d4b3e86
--- /dev/null
+++ b/testdata/lvs/inv.lvsdb
@@ -0,0 +1,239 @@
+#%lvsdb-klayout
+
+# Layout
+layout(
+ top(INVERTER)
+ unit(0.001)
+
+ # Layer section
+ # This section lists the mask layers (drawing or derived) and their connections.
+
+ # Mask layers
+ layer(l3 'NWELL (1/0)')
+ layer(l8 'POLY (5/0)')
+ layer(l9 'CONTACT (6/0)')
+ layer(l10 'METAL1 (7/0)')
+ layer(l11 'METAL1_LABEL (7/1)')
+ layer(l12 'VIA1 (8/0)')
+ layer(l13 'METAL2 (9/0)')
+ layer(l14 'METAL2_LABEL (9/1)')
+ layer(l15)
+ layer(l23)
+ layer(l31)
+
+ # Mask layer connectivity
+ connect(l3 l3)
+ connect(l8 l8 l9)
+ connect(l9 l8 l9 l10 l23 l31)
+ connect(l10 l9 l10 l11 l12)
+ connect(l11 l10 l11)
+ connect(l12 l10 l12 l13)
+ connect(l13 l12 l13 l14)
+ connect(l14 l13 l14)
+ connect(l15 l15)
+ connect(l23 l9 l23)
+ connect(l31 l9 l31)
+
+ # Global nets and connectivity
+ global(l3 NWELL)
+ global(l15 SUBSTRATE)
+
+ # Device class section
+ class(PMOS MOS4)
+ class(NMOS MOS4)
+
+ # Device abstracts section
+ # Device abstracts list the pin shapes of the devices.
+ device(D$PMOS PMOS
+ terminal(S
+ rect(l23 (-575 -750) (450 1500))
+ )
+ terminal(G
+ rect(l8 (-125 -750) (250 1500))
+ )
+ terminal(D
+ rect(l23 (125 -750) (450 1500))
+ )
+ terminal(B
+ rect(l3 (-125 -750) (250 1500))
+ )
+ )
+ device(D$NMOS NMOS
+ terminal(S
+ rect(l31 (-575 -450) (450 900))
+ )
+ terminal(G
+ rect(l8 (-125 -450) (250 900))
+ )
+ terminal(D
+ rect(l31 (125 -450) (450 900))
+ )
+ terminal(B
+ rect(l15 (-125 -450) (250 900))
+ )
+ )
+
+ # Circuit section
+ # Circuits are the hierarchical building blocks of the netlist.
+ circuit(INVERTER
+
+ # Nets with their geometries
+ net(1 name(IN)
+ rect(l8 (900 50) (250 1050))
+ rect(l8 (-250 0) (250 3100))
+ rect(l8 (-250 0) (250 1650))
+ rect(l8 (-800 -3100) (550 400))
+ rect(l9 (-450 -300) (200 200))
+ rect(l10 (-300 -300) (400 400))
+ rect(l11 (-201 -201) (2 2))
+ )
+ net(2 name(VSS)
+ rect(l9 (550 300) (200 200))
+ rect(l9 (-200 300) (200 200))
+ rect(l10 (-250 -950) (300 1050))
+ rect(l12 (-250 -950) (200 200))
+ rect(l12 (-200 300) (200 200))
+ rect(l13 (-750 -850) (2000 1000))
+ rect(l14 (-101 -851) (2 2))
+ rect(l31 (-1451 49) (450 900))
+ )
+ net(3 name(VDD)
+ rect(l9 (550 4350) (200 200))
+ rect(l9 (-200 300) (200 200))
+ rect(l9 (-200 300) (200 200))
+ rect(l10 (-250 -1300) (300 1600))
+ rect(l12 (-250 -800) (200 200))
+ rect(l12 (-200 300) (200 200))
+ rect(l13 (-750 -850) (2000 1000))
+ rect(l14 (-151 -851) (2 2))
+ rect(l23 (-1401 -851) (450 1500))
+ )
+ net(4 name(OUT)
+ rect(l9 (1300 4350) (200 200))
+ rect(l9 (-200 300) (200 200))
+ rect(l9 (-200 300) (200 200))
+ rect(l9 (-200 -5250) (200 200))
+ rect(l9 (-200 300) (200 200))
+ rect(l10 (-250 3250) (300 1400))
+ rect(l10 (-300 -4600) (300 3200))
+ rect(l10 (-300 -2900) (450 400))
+ rect(l10 (-450 -1550) (300 850))
+ rect(l11 (-51 499) (2 2))
+ rect(l23 (-351 2649) (450 1500))
+ rect(l31 (-450 -5500) (450 900))
+ )
+ net(5 name(NWELL)
+ rect(l3 (0 2950) (2000 3200))
+ )
+ net(6 name(SUBSTRATE))
+
+ # Devices and their connections
+ device(1 D$PMOS
+ location(1025 4950)
+ param(L 0.25)
+ param(W 1.5)
+ param(AS 0.675)
+ param(AD 0.675)
+ param(PS 3.9)
+ param(PD 3.9)
+ terminal(S 3)
+ terminal(G 1)
+ terminal(D 4)
+ terminal(B 5)
+ )
+ device(2 D$NMOS
+ location(1025 650)
+ param(L 0.25)
+ param(W 0.9)
+ param(AS 0.405)
+ param(AD 0.405)
+ param(PS 2.7)
+ param(PD 2.7)
+ terminal(S 2)
+ terminal(G 1)
+ terminal(D 4)
+ terminal(B 6)
+ )
+
+ )
+)
+
+# Reference netlist
+reference(
+
+ # Device class section
+ class(PMOS MOS4)
+ class(NMOS MOS4)
+
+ # Circuit section
+ # Circuits are the hierarchical building blocks of the netlist.
+ circuit(INVERTER
+
+ # Nets
+ net(1 name(VSS))
+ net(2 name(IN))
+ net(3 name(OUT))
+ net(4 name(NWELL))
+ net(5 name(SUBSTRATE))
+ net(6 name(VDD))
+
+ # Outgoing pins and their connections to nets
+ pin(1)
+ pin(2)
+ pin(3)
+ pin(4)
+ pin(5)
+ pin(6)
+
+ # Devices and their connections
+ device(1 PMOS
+ name(P)
+ param(L 0.25)
+ param(W 1.5)
+ param(AS 0)
+ param(AD 0)
+ param(PS 0)
+ param(PD 0)
+ terminal(S 6)
+ terminal(G 2)
+ terminal(D 3)
+ terminal(B 4)
+ )
+ device(2 NMOS
+ name(N)
+ param(L 0.25)
+ param(W 0.9)
+ param(AS 0)
+ param(AD 0)
+ param(PS 0)
+ param(PD 0)
+ terminal(S 3)
+ terminal(G 2)
+ terminal(D 1)
+ terminal(B 5)
+ )
+
+ )
+)
+
+# Cross reference
+xref(
+ circuit(INVERTER INVERTER match
+ xref(
+ net(1 2 match)
+ net(5 4 match)
+ net(4 3 match)
+ net(6 5 match)
+ net(3 6 match)
+ net(2 1 match)
+ pin(() 0 match)
+ pin(() 1 match)
+ pin(() 2 match)
+ pin(() 3 match)
+ pin(() 4 match)
+ pin(() 5 match)
+ device(2 2 match)
+ device(1 1 match)
+ )
+ )
+)
diff --git a/testdata/lvs/inv_layout.cir b/testdata/lvs/inv_layout.cir
new file mode 100644
index 000000000..c75dde3e8
--- /dev/null
+++ b/testdata/lvs/inv_layout.cir
@@ -0,0 +1,15 @@
+* Extracted by KLayout
+
+* cell INVERTER
+.SUBCKT INVERTER
+* net 1 IN
+* net 2 VSS
+* net 3 VDD
+* net 4 OUT
+* net 5 NWELL
+* net 6 SUBSTRATE
+* device instance $1 r0 *1 1.025,4.95 PMOS
+M$1 3 1 4 5 PMOS L=0.25U W=1.5U AS=0.675P AD=0.675P PS=3.9U PD=3.9U
+* device instance $2 r0 *1 1.025,0.65 NMOS
+M$2 2 1 4 6 NMOS L=0.25U W=0.9U AS=0.405P AD=0.405P PS=2.7U PD=2.7U
+.ENDS INVERTER