From 2f6aae7204fa7518601cd93f12eb2ea9bf164873 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Jul 2019 08:52:40 +0200 Subject: [PATCH] WIP: refactoring, added first tests for LVS --- src/drc/drc/built-in-macros/drc_install.lym | 48 ++ src/drc/drc/built-in-macros/install.lym | 50 -- src/drc/drc/drcResources.qrc | 5 +- src/klayout.pro | 5 +- src/lvs/lvs.pro | 6 + .../lvs}/built-in-macros/_lvs_engine.rb | 0 .../lvs}/built-in-macros/_lvs_netter.rb | 0 src/lvs/lvs/built-in-macros/lvs_install.lym | 48 ++ .../lvs}/built-in-macros/lvs_interpreters.lym | 0 src/lvs/lvs/lvs.pro | 22 + src/lvs/lvs/lvsCommon.h | 51 ++ src/lvs/lvs/lvsForceLink.cc | 34 + src/lvs/lvs/lvsForceLink.h | 40 ++ src/lvs/lvs/lvsResources.qrc | 8 + src/lvs/unit_tests/lvsBasicTests.cc | 162 +++++ src/lvs/unit_tests/lvsSimpleTests.cc | 623 ++++++++++++++++++ src/lvs/unit_tests/unit_tests.pro | 16 + testdata/lvs/inv.lvsdb | 239 +++++++ testdata/lvs/inv_layout.cir | 15 + 19 files changed, 1316 insertions(+), 56 deletions(-) create mode 100644 src/drc/drc/built-in-macros/drc_install.lym delete mode 100644 src/drc/drc/built-in-macros/install.lym create mode 100644 src/lvs/lvs.pro rename src/{drc/drc => lvs/lvs}/built-in-macros/_lvs_engine.rb (100%) rename src/{drc/drc => lvs/lvs}/built-in-macros/_lvs_netter.rb (100%) create mode 100644 src/lvs/lvs/built-in-macros/lvs_install.lym rename src/{drc/drc => lvs/lvs}/built-in-macros/lvs_interpreters.lym (100%) create mode 100644 src/lvs/lvs/lvs.pro create mode 100644 src/lvs/lvs/lvsCommon.h create mode 100644 src/lvs/lvs/lvsForceLink.cc create mode 100644 src/lvs/lvs/lvsForceLink.h create mode 100644 src/lvs/lvs/lvsResources.qrc create mode 100644 src/lvs/unit_tests/lvsBasicTests.cc create mode 100644 src/lvs/unit_tests/lvsSimpleTests.cc create mode 100644 src/lvs/unit_tests/unit_tests.pro create mode 100644 testdata/lvs/inv.lvsdb create mode 100644 testdata/lvs/inv_layout.cir 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